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>()第二个重载不可行的情况,因为模板参数替换失败,所以只有第一个重载是可行的,所以没有歧义。

对于has_begin_member<std::vector<int>>()案例替换成功,因此有两个可行的功能。

13.3.2 [over.match.viable]:

  • 如果列表中有m个参数,则所有具有恰好m个参数的候选函数都是可行的。
  • 具有少于m个参数的候选函数只有在其参数列表中有省略号时才可行(8.3.5)。出于重载决议的目的,任何没有相应参数的参数都被认为是“匹配省略号”(13.3.3.1.3)。
  • 仅当(m+1) -st 参数具有默认参数 (8.3.6)时,具有多于m个参数的候选函数才是可行的。出于重载决议的目的,参数列表在右侧被截断,因此正好有m个参数。

在这种情况下, m为零,第一个重载是可行的(通过第二个项目符号),第二个重载也是可行的(通过第三个项目符号),但是为了重载决议的目的,具有默认参数的参数被忽略,因此通过比较找到最佳可行功能:

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 回答