-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>&&’
繰り返しますが、ここでの質問は「何が起こっているのですか?」です。テンプレート仕様を削除することで問題を解決できることは知っていますが、ここで何が失敗しているかを知りたいだけです。
これは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
は左辺値式です(これは一時的なものではなく、移動されません)。これは、関数テンプレートが引数と一致しないことを意味し、エラーが発生する理由です。
では、テンプレート引数リストにT
とU
を明示的に指定しないと、なぜ機能するのでしょうか?つまり、右辺値参照パラメーターはテンプレートでは特別です。 reference collapsingと呼ばれる言語機能に一部起因して、タイプA&&
の右辺値参照パラメーター(A
はテンプレート型パラメーター)は、あらゆる種類のA
にバインドできます。
A
が左辺値、右辺値、const修飾、volatile修飾、または非修飾のいずれであっても、A&&
はそのオブジェクトにバインドできます(ここでも、A
自体がテンプレートパラメーターである場合のみ)。
あなたの例では、呼び出しを行います:
make_pair(s, 7)
ここで、s
はstd::string
型の左辺値であり、7
はint
型の右辺値です。関数テンプレートのテンプレート引数を指定しないため、引数が何であるかを判断するためにテンプレート引数の推論が実行されます。
左辺値s
をT&&
にバインドするには、コンパイラーはT
をstd::string&
であると推定し、std::string& &&
型の引数を生成します。ただし、参照への参照はないため、この「二重参照」は折りたたまれてstd::string&
になります。 s
は一致します。
7
をU&&
にバインドするのは簡単です。コンパイラーはU
をint
であると推測し、右辺値であるためint&&
に正常にバインドする7
型のパラメーターを生成できます。
これらの新しい言語機能には多くの微妙な点がありますが、1つの単純なルールに従えば、それは非常に簡単です。
テンプレートの引数を関数の引数から推測できる場合は、推測させてください。絶対に必要な場合を除き、引数を明示的に指定しないでください。
コンパイラに一生懸命やらせてください。99.9%の時間で、とにかく望みどおりになります。望んでいない場合は、通常、コンパイルエラーが発生しますが、これは簡単に特定して修正できます。