4

次の典型的な SFINAE テスト関数を検討してください (型にbegin()メンバー関数があるかどうかをチェックします) 。

    template <class> constexpr bool
has_begin_member (...) { return false; }

    template <class T> constexpr bool
has_begin_member (decltype (std::declval <T>().begin ())* = 0) { 
    return true;
}

引数で呼び出すことができます:

has_begin_member <int> (0); // yields false

ただし、引数なし:

has_begin_member <int> (); // compilation error

それは次のあいまいさにつながります。

error: call of overloaded 'has_begin_member()' is ambiguous
note: candidates are:
note: constexpr bool has_begin_member(...)
note: constexpr bool has_begin_member(decltype (declval<T>().begin())*)

その場合、「省略記号のトリック」が機能しないのはなぜですか?

編集:完全なプログラム:

#include <utility>
#include <vector>

    template <class> constexpr bool
has_begin_member (...) { return false; }

    template <class T> constexpr bool
has_begin_member (decltype (std::declval <T>().begin ())* = 0) { 
    return true;
}


static_assert (!has_begin_member <int> (0), "broken");
static_assert (has_begin_member <std::vector <int>> (0), "broken");

static_assert (!has_begin_member <int> (), "broken");
static_assert (has_begin_member <std::vector <int>> (), "broken");

    int 
main (){}

コンパイル:

g++ -std=c++11 -o toto ./toto.cpp
./toto.cpp:17:58: error: call of overloaded 'has_begin_member()' is ambiguous
./toto.cpp:17:58: note: candidates are:
./toto.cpp:5:5: note: constexpr bool has_begin_member(...) [with <template-parameter-1-1> = std::vector<int>]
./toto.cpp:8:5: note: constexpr bool has_begin_member(decltype (declval<T>().begin())*) [with T = std::vector<int>; decltype (declval<T>().begin()) = __gnu_cxx::__normal_iterator<int*, std::vector<int> >]
4

1 に答える 1

3

テンプレート引数の置換が失敗したためhas_begin_member<int>()、2 番目のオーバーロードが実行可能ではない場合、最初のオーバーロードのみが実行可能であるため、あいまいさはありません。

このhas_begin_member<std::vector<int>>()場合、置換は成功するため、実行可能な関数が 2 つあります。

13.3.2 [over.match.viable]:

  • リストにm 個の引数がある場合、正確にm 個のパラメーターを持つすべての候補関数が実行可能です。
  • パラメータがm未満の候補関数は、パラメータ リストに省略記号がある場合にのみ実行可能です (8.3.5)。オーバーロード解決の目的で、対応するパラメーターがない引数は、「省略記号に一致する」と見なされます (13.3.3.1.3) 。
  • m 個を超えるパラメーターを持つ候補関数は、 (m+1) -st パラメーターにデフォルトの引数 (8.3.6) がある場合にのみ実行可能です。オーバーロード解決の目的で、パラメーター リストの右側が切り捨てられ、正確にm 個のパラメーターが存在するようになります。

この場合、 mは 0 であり、最初のオーバーロードは (2 番目の箇条書きによって) 実行可能であり、2 番目のオーバーロードも (3 番目の箇条書きによって) 実行可能ですが、オーバーロードの解決のために、既定の引数を持つパラメータは無視されるため、最適な実行可能な関数は、以下を比較して見つけられます。

template<> constexpr bool has_begin_member<vector<int>>(...);
template<> constexpr bool has_begin_member<vector<int>>();

次のように、明らかにあいまいです。

int f(...);
int f();

int i = f();  // error

どちらの関数を呼び出すのにも必要な変換シーケンスがないため、(13.3.3.2 [over.ics.rank] のルールを使用して) どちらが「より良い変換シーケンス」を持つかという点でランク付けすることはできません。あいまいです。

于 2012-08-24T13:43:57.000 に答える