質問するログイン新規登録

Q&A

1回答

136閲覧

std::tuple_element の特殊化方法

fana

総合スコア12498

コンパイルエラー

コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2026/09/16 08:44

編集2026/09/16 10:41

0

0

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_size
  • std::tuple_element
  • get

なので,それを書いてみたつもりです:

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 の特殊化をうまくやれていないのかと思うのですが,実際どうすれば良いのかわかりません.

  1. 何故エラーになっているのでしょうか?
  2. どう直せば良いのでしょうか?

(追記)コード全体:

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 が必要になる,みたいな……?
全く自信無いですが.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

guest

回答1

0

あらためて調べてみるとなかなかややこしいですね。

構造化束縛のときには対象となるオブジェクトに対して暗黙に get<i>(e) のような形で呼び出され、エンティティ e が左辺値参照のときは左辺値だがそうでないときは xvalue となるという規則に引っかかっているようです。

つまりエンティティ e とは質問の例では v のことで、 v は参照ではないので xvalue であると解され、 xvalue (右辺値の一種) は

cpp

1template <std::size_t I, size_t Dim, typename Val_t> 2Val_t& get(V<Dim, Val_t>& v) noexcept { return v[I]; }

のほうの get では受け取れません。

なので

cpp

1template <std::size_t I, size_t Dim, typename Val_t> 2const Val_t& get(const V<Dim, Val_t>& v) noexcept { return v[I]; }

のほうで受け取られて (仮引数が const の左辺値参照だったら右辺値も受け取れる規則) しまい、つまり結果の型も const Val_t& であることになります。

結果の型 (質問の例で言う xy) の推論は std::tuple_element が使われることになっていて、これでは const がついてないことになっているのに get のほうでは const 付きなので型の整合性がとれないというのがエラーの理由です。

なのでこの質問の例をなんとかするなら

cpp

1template <std::size_t I, size_t Dim, typename Val_t> 2Val_t& get(V<Dim, Val_t>&& v) noexcept { return v[I]; }

という定義を付け足すのが最も単純な解です。 試したこと(2)のやり方でとりあえず問題ないということです。


プログラム上の字面では v 自体は左辺値なのですから左辺値参照で受け取れるのが自然に思うのですが、左辺値参照のときのみ左辺値として渡され、そうでないときは内部的に一時的なコピーが作られてそれが右辺値として渡されるみたいな感じですね。

投稿2026/09/16 15:32

SaitoAtsushi

総合スコア5906

fana

2026/09/17 01:55

> auto [x,y] = v; とか書くと //vのコピーとして作られた一時オブジェクトが get に渡されるような雰囲気 double x = get<0>( V<2,double>( v ) ); double y = get<1>( V<2,double>( v ) ); みたいなこと になる……という感じ,ですかね. ただし,この2行な記述とはちがって…… `V<2,double>( v )` なる一時オブジェクトっぽいもの(:エンティティ e)は1つだけ作られて,その寿命は `auto [x,y] = v;` を書いた場所に変数を定義したのと同じになる ……というような.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.25%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問