指定されたテンプレートパラメータを持つC ++ 11make_pairはコンパイルされません


84

-std = c ++ 11を有効にしてg ++ 4.7(後のスナップショットの1つ)で遊んでいました。私は既存のコードベースのいくつかをコンパイルしようとしましたが、失敗した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が(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を削除しても、問題なくコードでコンパイルできます。

6
素晴らしい質問です。まだと同様C ++ 11の微妙な破壊変化の別の例で破断変化std::vector建設。少なくともこれは、セマンティクスのサイレントな変更ではなく、コンパイラエラーを生成します。
James McNellis 2012年

1
整数変数iがある場合。iと他のオブジェクトとペアにしたい。どのくらい正確にmakepairと呼ぶべきですか。1)make_pair <* i、obj> 2)int && j = i; make_pair <j、obj>?どちらも機能していません。それを行う正しい方法は何ですか?
PHcoDer 2016

回答:


135

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

C ++ 11std::make_pairは、typeT&&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か。要するに、右辺値参照パラメーターはテンプレートでは特別です。呼ばれる言語機能に一部起因参照が崩壊、タイプの右辺値参照パラメータA&&Aテンプレート型パラメータであり、あらゆる種類のに特異的に結合することができますA

Aが左辺値、右辺値、定数修飾、揮発性修飾、または非修飾のいずれであるかは関係ありませんA&&(これも、Aそれ自体がテンプレートパラメータである場合に限ります)。

あなたの例では、次のように呼び出します。

make_pair(s, 7)

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

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

バインドにそれのシンプル7U&&:コンパイラが推測できるUようにint型のパラメータ得int&&に正常に結合し、7それは右辺値であるために。

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

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

コンパイラに大変な作業を任せてください。99.9%の確率で、とにかくあなたが望んでいたものになります。希望どおりでない場合は、通常、簡単に識別して修正できるコンパイルエラーが発生します。


6
これは非常に優れた包括的な説明です。ありがとうございました!
vmpstr 2012年

1
@ James-私が読むべき別の記事または回答からの「1つの単純なルール」ですか?
Michael Burr 2012年

4
@MichaelBurr:いや、私はそれを作りました。:-)だから、それが本当だといいのですが!私はそれが本当だと思います...そのルールは私にとってほとんどいつもうまくいきます。
James McNellis 2012年

1
@ジェームズ:ありがとう。その周りの「引用ボックス」は、もともと他の場所で書かれたものだったのではないかと思いました。この答えは本当に有益でした、そして私は私がどこか他の場所で何かを逃していないことを確認したかっただけです。
Michael Burr 2012年

2
これはタプルにも当てはまりますか?
フェルッチオ2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.