C++初心者です.
こんなの↓に対して,構造化束縛というのを使えるようにしたいです.
C++
1//「座標」とか「ベクトル」みたいなやつ. 2// Val_t 型の要素を Dim 個持っている. 3template< size_t Dim, typename Val_t > 4class V 5{ 6public: 7 Val_t Components[Dim]; 8 9 Val_t &operator[](size_t i){ return Components[i]; } 10 const Val_t &operator[](size_t i) const { return Components[i]; } 11};
例えば以下のようなことがしたい:
C++
1V<2,double> v{ 5.2, 9.999 }; 2auto [x,y] = v; 3std::cout << x << " , " << y << std::endl;
調べた結果,以下の物の特殊化を用意する必要がある,という話のようです.
std::tuple_sizestd::tuple_elementget
なので,それを書いてみたつもりです:
C++
1//typle_size 2template< size_t Dim, typename Val_t > 3struct std::tuple_size< V<Dim,Val_t> >{ static constexpr size_t value = Dim; }; 4 5//tuple_element 6//(とりあえず I が範囲外という場合を考えていませんが) 7template< std::size_t I, size_t Dim, typename Val_t > 8struct std::tuple_element< I, V<Dim,Val_t> >{ using type = Val_t; }; 9 10//get 11//(こっちもとりあえず I が範囲外という場合を考えていません) 12template< std::size_t I, size_t Dim, typename Val_t > 13Val_t& get( V<Dim,Val_t>& v ) noexcept { return v[I]; } 14 15template< std::size_t I, size_t Dim, typename Val_t > 16const Val_t& get( const V<Dim,Val_t>& v ) noexcept { return v[I]; }
……が,前記の構造化束縛の部分 auto [x,y] = v; のコンパイルが通りません.
(コンパイルエラーの文言を下記コード内に示します)
C++
1//Test 2int main() 3{ 4 //get はできてるっぽい 5 V<2,double> v{ 5.2, 9.999 }; 6 get<0>( v ) = 7; 7 get<1>( v ) = -6.252; 8 std::cout << get<0>(v) << " , " << get<1>(v) << std::endl; 9 10#if 0 11 //Visual Studio 2022 では以下のエラーが出る: 12 // > E0433 13 // > 型 "std::tuple_element<0Ui64, V<2Ui64, double>>::type &" (aka "double &") の参照を 14 // > 型 "const double" の初期化子にバインドするときに修飾子がドロップされました 15 //> C2440 16 // > '初期化中': 'const Val_t' から 'Val_t &' に変換できません。 17 18 //OnlineGDB というところでも試したが,同じことを言われている模様: 19 // > error: binding reference of type ‘std::tuple_element<0, V<2, double> >::type&’ {aka ‘double&’} to ‘const double’ discards qualifiers 20 auto [x,y] = v; 21 std::cout << x << " , " << y << std::endl; 22#endif 23 24 //こっちはOK 25 auto &[x,y] = v; 26 std::cout << x << " , " << y << std::endl; 27 28 return 0; 29}
おそらく, std::tuple_element の特殊化をうまくやれていないのかと思うのですが,実際どうすれば良いのかわかりません.
- 何故エラーになっているのでしょうか?
- どう直せば良いのでしょうか?
(追記)コード全体:
C++
1#include <iostream> 2#include <tuple> 3 4template< size_t Dim, typename Val_t > 5class V 6{ 7public: 8 Val_t Components[Dim]; 9 10 Val_t &operator[](size_t i){ return Components[i]; } 11 const Val_t &operator[](size_t i) const { return Components[i]; } 12}; 13 14template< size_t Dim, typename Val_t > 15struct std::tuple_size< V<Dim,Val_t> >{ static constexpr size_t value = Dim; }; 16 17template< std::size_t I, size_t Dim, typename Val_t > 18struct std::tuple_element< I, V<Dim,Val_t> >{ using type = Val_t; }; 19 20template< std::size_t I, size_t Dim, typename Val_t > 21struct std::tuple_element< I, const V<Dim,Val_t> >{ using type = const Val_t; }; 22 23 24template< std::size_t I, size_t Dim, typename Val_t > 25Val_t& get( V<Dim,Val_t>& v ) noexcept { return v[I]; } 26 27template< std::size_t I, size_t Dim, typename Val_t > 28const Val_t& get( const V<Dim,Val_t>& v ) noexcept { return v[I]; } 29 30 31//Test 32int main() 33{ 34 V<2,double> v{ 5.2, 9.999 }; 35 get<0>( v ) = 7; 36 get<1>( v ) = -6.252; 37 std::cout << get<0>(v) << " , " << get<1>(v) << std::endl; 38 39#if 1 40 //E0433 41 // 型 "std::tuple_element<0Ui64, V<2Ui64, double>>::type &" (aka "double &") の参照を 42 // 型 "const double" の初期化子にバインドするときに修飾子がドロップされました 43 //C2440 44 // '初期化中': 'const Val_t' から 'Val_t &' に変換できません。 45 { 46 auto [x,y] = v; 47 std::cout << x << " , " << y << std::endl; 48 } 49#endif 50 51 { 52 //こっちはOK 53 auto &[x,y] = v; 54 std::cout << x << " , " << y << std::endl; 55 } 56 57 std::cout << "(END)" << std::endl; 58 return 0; 59}
試したこと
エラーメッセージの雰囲気から,
「 const double の初期化に double & を用いようとしている」みたいな話かと見える(? だとしても何が問題なのか謎だが…)ので,
以下のようなものを追加してみたのですが,全くエラーメッセージも変わりませんでした.
C++
1//tuple_element の const 版が必要ということだろうか? 2//……しかしそれをどう書けばいいのか? 3//これだとダメっぽい. 4template< std::size_t I, size_t Dim, typename Val_t > 5struct std::tuple_element< I, const V<Dim,Val_t> >{ using type = const Val_t; };
試したこと(2)
とても私のような素人に読める文章ではないが,ここ をなんとなく見た感じ……
ダメなのは tuple_element じゃなくて get の方なのかも.
C++
1auto [x,y] = v; 2const auto [x,y] = v; 3auto &[x,y] = v; 4const auto &[x,y] = v;
のどれなのかによって用いられる get の引数の型が異なるんだけど,必要なやつを用意してなかったから問題が起きている……みたいな?
下記の (3)や(4) を追加するとコンパイルエラーが消えるようだ.
C++
1//(1) 2// auto &[x,y] = v; で使われる 3template< std::size_t I, size_t Dim, typename Val_t > 4Val_t& get( V<Dim,Val_t>& v ) noexcept { return v[I]; } 5 6//(2) 7// const auto &[x,y] = v; で使われる 8// ※(4)が無い場合には const auto [x,y] = v; でも使われる 9template< std::size_t I, size_t Dim, typename Val_t > 10const Val_t& get( const V<Dim,Val_t>& v ) noexcept { return v[I]; } 11 12//--- 13//以下の (3),(4) を追加した 14//(これらの戻り値の型がどうあるべきなのかがさっぱり謎だが……) 15//--- 16 17//(3) 18// auto [x,y] = v; で使われる 19template< std::size_t I, size_t Dim, typename Val_t > 20Val_t& get( V<Dim,Val_t>&& v ) noexcept { return v[I]; } 21 22//(4) 23// const auto [x,y] = v; で使われる 24template< std::size_t I, size_t Dim, typename Val_t > 25const Val_t& get( const V<Dim,Val_t>&& v ) noexcept { return v[I]; }
イマイチ話がわかりませんが, & 無しの形の auto [x,y] = v; や const auto [x,y] = v; だと,
uniquely-named variable (here denoted by e)
とかいう謎の変数が( v のコピーとして)その場で作られる(で,それがその場で get の引数になる)ことにより,引数が && な get が必要になる,みたいな……?
全く自信無いですが.
2026/09/17 01:55