考虑以下典型的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> >]
答案 0 :(得分: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参数具有(m + 1) -st参数时,具有 m 参数以上的候选函数才可行 默认参数(8.3.6)。出于重载解析的目的,将截断参数列表 在右边,这样就有 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]中的规则)这意味着它们含糊不清。