2012-08-24 28 views
4

考虑以下典型SFINAE测试功能(它检查是否一个类型具有begin()成员函数)SFINAE:如果调用无参数暧昧过载

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 

它会导致以下不确定性:

为什么在这种情况下“省略技巧”不起作用?

编辑:完整的程序:

#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

你使用什么编译器? –

+0

g ++(GCC)4.7.1 20120721(预发布) – manu

+1

我认为这看起来像GCC诊断,但它对我来说也适用于G ++ 4.5,4.6,4.7,4.8和Clang ++。请显示一个完整的示例程序来演示错误,我显然不会像您一样测试相同的东西。 –

回答

3

对于has_begin_member<int>()情况下,第二过载是不可行的,因为模板参数替换失败,所以才有了第一个重载是可行的,所以没有歧义。

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

13.3.2 [over.match.viable]:

  • 如果有参数列表中的,具有正好参数是可行的所有候选的功能。
  • 只有在其参数 列表(8.3.5)中有一个省略号时,参数才具有可行性,但参数少于m。出于重载解析的目的,任何没有相应参数的参数都被认为是“匹配省略号”(13.3.3.1.3)。
  • 具有多于参数候选功能是可行的只有当(M + 1) -st参数具有 默认参数(8.3.6)。出于超负荷分辨率的目的,参数列表在右侧被截断为 ,以便确切地具有参数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]中的规则),意味着他们不明确。

+0

好的,因为在那种情况下没有转换,所以省略号的东西不起任何作用,对吧? – manu

+0

是的,请参阅我在答案末尾添加的额外示例。 –