SFINAE:如果没有参数调用,则会出现模糊的重载

时间:2012-08-24 12:32:47

标签: c++ c++11 sfinae

考虑以下典型的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> >]

1 个答案:

答案 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]中的规则)这意味着它们含糊不清。