C++ SFINAE:如果在没有参数的情况下调用,则重载不明确

C++ SFINAE:如果在没有参数的情况下调用,则重载不明确,c++,c++11,sfinae,C++,C++11,Sfinae,考虑以下典型的SFINAE测试函数(它检查类型是否具有begin()成员函数) template constexpr bool has_begin_成员(…){return false;} 模板constexpr bool has_begin_成员(decltype(std::declval().begin())*=0){ 返回true; } 我可以用一个论点来形容它: has_begin_member <int> (0); // yields false 有_begin_成员(

考虑以下典型的SFINAE测试函数(它检查类型是否具有
begin()
成员函数)

template constexpr bool
has_begin_成员(…){return false;}
模板constexpr bool
has_begin_成员(decltype(std::declval().begin())*=0){
返回true;
}
我可以用一个论点来形容它:

has_begin_member <int> (0); // yields false
有_begin_成员(0);//产生错误
但没有任何论据:

has_begin_member <int> (); // compilation error
有_begin_member();//编译错误
它导致以下歧义:

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())*)
错误:重载“has\u begin\u member()”的调用不明确
注:候选人包括:
注意:constexpr bool有begin成员(…)
注意:constexpr bool有begin成员(decltype(declval().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 (){}
#包括
#包括
模板constexpr bool
has_begin_成员(…){return false;}
模板constexpr bool
has_begin_成员(decltype(std::declval().begin())*=0){
返回true;
}
静态断言(!具有开始成员(0),“已断开”);
静态断言(具有开始成员(0),“已断开”);
静态断言(!has_begin_member(),“breaked”);
静态断言(有begin成员(),“breaked”);
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> >]
g++-std=c++11-o toto./toto.cpp
./toto.cpp:17:58:错误:重载“has\u begin\u member()”的调用不明确
/toto.cpp:17:58:注:候选人为:
./toto.cpp:5:5:注意:constexpr bool有begin成员(…)[with=std::vector]
/toto.cpp:8:5:注意:constexpr bool有begin成员(decltype(declval().begin())*[with T=std::vector;decltype(declval().begin())=\u gnu\u cxx:\u normal\u迭代器]

对于
has\u begin\u member()
情况,第二个重载不可行,因为模板参数替换失败,所以只有第一个重载可行,因此没有歧义


对于
has\u begin\u member()
大小写替换成功,因此有两个可行的函数

13.3.2[超过匹配可行]:

  • 如果列表中有m个参数,则所有具有m个参数的候选函数都是可行的
  • 参数少于m的候选函数只有在其参数中有省略号时才可行 清单(8.3.5)。为了重载解析的目的,没有相应参数的任何参数 参数被视为“匹配省略号”(13.3.3.1.3)
  • 只有当(m+1)-st参数具有 默认参数(8.3.6)。出于重载解析的目的,参数列表被截断 在右边,正好有m个参数
在这种情况下,m为零,第一个重载是可行的(通过第二个项目符号),第二个重载也是可行的(通过第三个项目符号),但为了重载解析的目的,忽略带有默认参数的参数,因此通过比较找到最佳可行函数:

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

调用任何一个函数都不需要转换序列,因此不能根据哪个函数比另一个函数“更好的转换序列”来对它们进行排序(使用13.3.3.2[over.ics.rank]中的规则),这意味着它们是不明确的。

g++(GCC)4.7.1 20120721(预发布)我认为这看起来像是一个GCC诊断,但是它对我来说在G++4.5、4.6、4.7、4.8和Clang++中都很好。请给出一个完整的示例程序来演示这个错误,我显然不是在测试与您相同的东西。啊哈,
has_begin_member()工作正常,它只有
有_begin_member()失败了,对不起,我发得有点快!编写
true
重载的更好方法:
template constexpr auto有\u begin\u member(T const*p)->decltype(p->begin(),bool()){return true;}
调用
有\u begin\u member(0)
。好的,因为在这种情况下没有转换,所以省略号不起任何作用,对吧,请参阅我在答案末尾添加的额外示例。
template<> constexpr bool has_begin_member<vector<int>>(...);
template<> constexpr bool has_begin_member<vector<int>>();
int f(...);
int f();

int i = f();  // error