1. ホーム
  2. c++

[解決済み] C++11 テンプレートパラメータを指定した make_pair がコンパイルされない

2023-05-27 05:13:49

質問

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

どのように解決するのですか?

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

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

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);

これらのパラメータタイプは両方ともrvalueの参照であることに注意してください。 したがって、これらはrvalueにのみバインドすることができます。 これは、渡す2番目の引数については問題ではありません。 7 はrvalue式だからです。 s は、しかし、lvalue式です(それは一時的ではなく、移動されていない)。 これは、関数テンプレートが引数にマッチしないことを意味し、それがエラーになる理由です。

では、なぜ明示的に何を指定しないときに動作するのかというと、それは TU はテンプレートの引数リストにあるのでしょうか? 要するに、rvalueの参照パラメータはテンプレートでは特別なのです。 という言語機能のせいもありますが 参照折りたたみ という言語機能により、rvalue の参照パラメータは A&& で、ここで A はテンプレート型パラメータで、任意の種類の A .

は関係ありません。 A が lvalue か rvalue か、const-qualified か volatile-qualified か、unqualified かは関係ありません。 A&& はそのオブジェクトにバインドすることができます(繰り返しますが、もし A がそれ自身テンプレート・パラメータである場合に限ります)。

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

make_pair(s, 7)

ここで s はlvalueで std::string であり 7 は型のrvalue int . 関数テンプレートのテンプレート引数を指定していないため,テンプレート引数推論が行われ,引数が何であるかが判明します.

を結合するには s を、l値である T&& に変換すると、コンパイラは Tstd::string& の引数を生成し、その引数は std::string& && . しかし、参照に対する参照は存在しないので、この二重参照は崩れて std::string& . s が一致します。

を束ねるのは簡単です。 7U&& : コンパイラが推論できるのは Uint のパラメータを生成します。 int&& という型のパラメータを生成し、それがうまく 7 はrvalueであるため、正常に結合されます。

これらの新しい言語機能には微妙な点がたくさんありますが、ある簡単なルールに従えば、とても簡単なことなのです。

もしテンプレートの引数が関数の引数から推測できるのであれば、それを推測させればよいのです。 絶対に必要でない限り、明示的に引数を提供する必要はありません。

コンパイラに難しい仕事をさせれば、99.9%はあなたが望んだとおりのものになるでしょう。 そして、99.9%はあなたが望んだとおりになります。あなたが望んだものでないとき、通常はコンパイルエラーが出ますが、これは識別して修正するのが簡単です。