web-dev-qa-db-ja.com

指定されたテンプレートパラメータを持つC ++ 11 make_pairがコンパイルされない

-std = c ++ 11を有効にして、g ++ 4.7(後のスナップショットの1つ)をいじっていました。私は既存のコードベースのいくつかをコンパイルしようとしましたが、失敗したケースがやや混乱しています。

誰かが何が起こっているのか説明できれば幸いです。

コードは次のとおりです。

#include <utility>
#include <iostream>
#include <vector>
#include <string>

int main ( )
{
    std::string s = "abc";

    // 1 ok
    std::pair < std::string, int > a = std::make_pair ( s, 7 );

    // 2 error on the next line
    std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );

    // 3 ok
    std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );

    return 0;
}

Make_pairはmeant(1)ケースとして使用されることを理解しています(タイプを指定する場合、(3)を使用することもできます)が、失敗する理由がわかりませんこの場合。

正確なエラーは次のとおりです。

test.cpp: In function ‘int main()’:
    test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
    test.cpp:11:83: note: candidate is:
    In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
                 from test.cpp:1:
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’

繰り返しますが、ここでの質問は「何が起こっているのですか?」です。テンプレート仕様を削除することで問題を解決できることは知っていますが、ここで何が失敗しているかを知りたいだけです。

  • g ++ 4.4は問題なくこのコードをコンパイルします。
  • -std = c ++ 11を削除しても、問題なくコードを使用してコンパイルできます。
75
vmpstr

これはstd::make_pairの使用方法ではありません。テンプレート引数を明示的に指定することは想定されていません。

C++ 11 std::make_pairは、タイプT&&およびU&&の2つの引数を取ります。ここで、TおよびUはテンプレート型パラメーターです。事実上、次のようになります(戻り値の型を無視):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

std::make_pairを呼び出して、テンプレートタイプの引数を明示的に指定すると、引数の推論は行われません。代わりに、型引数はテンプレート宣言に直接代入され、次のようになります。

[return type] make_pair(std::string&& argT, int&& argU);

これらのパラメータタイプはどちらも右辺値参照であることに注意してください。したがって、それらは右辺値にのみバインドできます。これは、渡す2番目の引数7の場合、問題ではありません。これは、右辺値式であるためです。ただし、sは左辺値式です(これは一時的なものではなく、移動されません)。これは、関数テンプレートが引数と一致しないことを意味し、エラーが発生する理由です。

では、テンプレート引数リストにTUを明示的に指定しないと、なぜ機能するのでしょうか?つまり、右辺値参照パラメーターはテンプレートでは特別です。 reference collapsingと呼ばれる言語機能に一部起因して、タイプA&&の右辺値参照パラメーター(Aはテンプレート型パラメーター)は、あらゆる種類のAにバインドできます。

Aが左辺値、右辺値、const修飾、volatile修飾、または非修飾のいずれであっても、A&&はそのオブジェクトにバインドできます(ここでも、A自体がテンプレートパラメーターである場合のみ)。

あなたの例では、呼び出しを行います:

make_pair(s, 7)

ここで、sstd::string型の左辺値であり、7int型の右辺値です。関数テンプレートのテンプレート引数を指定しないため、引数が何であるかを判断するためにテンプレート引数の推論が実行されます。

左辺値sT&&にバインドするには、コンパイラーはTstd::string&であると推定し、std::string& &&型の引数を生成します。ただし、参照への参照はないため、この「二重参照」は折りたたまれてstd::string&になります。 sは一致します。

7U&&にバインドするのは簡単です。コンパイラーはUintであると推測し、右辺値であるためint&&に正常にバインドする7型のパラメーターを生成できます。

これらの新しい言語機能には多くの微妙な点がありますが、1つの単純なルールに従えば、それは非常に簡単です。

テンプレートの引数を関数の引数から推測できる場合は、推測させてください。絶対に必要な場合を除き、引数を明示的に指定しないでください。

コンパイラに一生懸命やらせてください。99.9%の時間で、とにかく望みどおりになります。望んでいない場合は、通常、コンパイルエラーが発生しますが、これは簡単に特定して修正できます。

121
James McNellis