web-dev-qa-db-ja.com

ブレースで囲まれた初期化子リストから標準タプルに変換できませんでした

より大きなプロジェクトの一環として、私はstd::Tupleとテンプレート。次のコードを検討してください。

template <typename ...T> void foo(Tuple<T...> t) {}
void bar(Tuple<int, char> t) {}
Tuple<int, char> quxx() { return {1, 'S'}; }

int main(int argc, char const *argv[])
{
    foo({1, 'S'});           // error
    foo(make_Tuple(1, 'S')); // ok
    bar({1, 'S'});           // ok
    quxx();                  // ok
    return 0;
}

この答え によると、C++ 17はcopy-list-initializationからのタプル初期化をサポートしていますが、次のエラーが発生するため、そのようなサポートは制限されているようです(GCC 7.2.0):

main.cpp: In function 'int main(int, const char**)':
main.cpp:14:17: error: could not convert '{1, 'S'}' from '<brace-enclosed initializer list>' to 'std::Tuple<>'
     foo({1, 'S'}); // error
                 ^

このシナリオで中括弧で囲まれた構文を使用する方法はありますか?

Some Context:これは演算子のオーバーロードで使用されるため、タプルにバインドされており、多様性を利用できないと思います。ヒントは受け入れられます。

Extra:Clang 6も文句を言う

prog.cc:12:5: error: no matching function for call to 'foo'
    foo({1, 'S'});           // error
    ^~~
prog.cc:6:31: note: candidate function [with T = <>] not viable: cannot convert initializer list argument to 'Tuple<>'
template <typename ...T> void foo(Tuple<T...> t) {}
9
Samuele Pilleri

braced-init-listは、{1, 'S'}のように、実際には型を持ちません。テンプレートの推論のコンテキストでは、initializer_list<T>Tは関数テンプレートパラメーターです)に対して推論する場合、または対応するパラメーターが他の何かによって既に推論される場合にのみ、それらを使用できます。この場合、これら2つのことはどちらも当てはまりません。したがって、コンパイラーは...Tが何であるかを理解できません。

したがって、タイプを直接提供できます。

foo<int, char>({1, 'S'});

または、Tupleを自分で作成して、次のように渡すことができます。

foo(std::Tuple<int, char>(1, 'S')); // most explicit
foo(std::Tuple(1, 'S')); // via class template argument deduction

現在、ClassTemplate<Ts...>は、ClassTemplate<Us...>型の式またはそのようなものから継承する型からのみ推定できます。架空の提案では、さらに拡張して、式でクラステンプレート引数の演繹を実行して、演繹が成功するかどうかを確認することができます。この場合、{1, 'S'}Tuple<Ts...>ではありませんが、Tuple __var{1, 'S'}Tuple<int, char>を正常に推定できるため、機能します。このような提案は、次のような問題にも対処する必要があります... ClassTemplate<T, Ts...>またはその他のマイナーバリエーションを推定している場合、それはクラステンプレート引数の推定が許可するものではありません(ただし、多くの人が時々持っているものです)できることに興味を示した)。

今日はそのような提案を知りません。

6
Barry

この回答によると、C++ 17はcopy-list-initializationからのタプル初期化をサポートしていますが、次のエラーが発生するため、そのようなサポートは制限されているようです

問題は別です。

bar({1, 'S'})を呼び出すと、コンパイラーはbar()が_Tuple<int, char>_を受け取ることを知っているため、_1_をintおよび_'S'_として受け取ります。 charとして。

別の例を参照してください。

_void baz (std::Tuple<int> const &)
 { }
_

あなたは電話することができます

_baz(1);
_

コンパイラはbaz()が_std::Tuple<int>_を受け取ることを知っているため、_1_を取得して、タプル内のintを初期化します。

しかし、

_template <typename ...T>
void foo(Tuple<T...> t)
 { }
_

コンパイラーは_T..._タイプを認識しません。あなたが電話するとき

_foo({1, 'S'}); 
_

どの_T..._型がコンパイラを推定する必要がありますか?

少なくとも、2つの仮説が見られます:_T = int, char_または_T = std::pair<int, char>_;または_T = std::Tuple<int, char>_。

どの仮説がコンパイラーに従うべきですか?

つまり、_std::Tuple_をfoo()に渡すと、コンパイラはタプル内の型のリストを_T..._のリストとして受け入れます。しかし、何か他のものを渡した場合、コンパイラは正しい_std::Tuple_;を推定する必要があります。しかし、この場合、この控除は一意ではありません。だからエラー。

1
max66