这种涉及可变参数模板的SFINAE技术是否有效?

时间:2015-07-01 00:56:56

标签: c++ templates c++11 variadic-templates sfinae

libstdc ++实现std::experimental::optional uses一种SFINAE技术,似乎与gcc一起使用,但不与clang一起使用。

我已将其缩减为以下最小例子:

// Standard enable_if class
template <bool> struct enable_if {};
template <> struct enable_if<true> { typedef int type; };

// An example trait
template <typename> struct trait { static const bool value = true; };

// Overload to call if the trait is false
template<typename T, typename enable_if<!trait<T>::value>::type...>
void foo(T);

// Overload to call if the trait is true
template<typename T, typename enable_if<trait<T>::value>::type...>
void foo(T);

// Call site
void bar() {
    foo(0);
}

这用gcc编译,但不用clang编译。 Clang的错误是:

test.cpp:18:5: error: call to 'foo' is ambiguous
    foo(0);
    ^~~
test.cpp:11:6: note: candidate function [with T = int, $1 = <>]
void foo(T);
     ^
test.cpp:14:6: note: candidate function [with T = int, $1 = <>]
void foo(T);
     ^

显然,gcc将第一个重载作为候选者丢弃,因为它在将T = int转换为typename enable_if<!trait<T>::value>::type时遇到替换失败。

另一方面,Clang似乎跳过执行该替换,可能是因为它意识到绑定到该参数包的模板参数为零。因此,它不会遇到替换失败,并且第一次过载仍然可行。

谁是对的?

1 个答案:

答案 0 :(得分:0)

我没有太多经验来解释C ++规范关于模板参数推导的内容,但我会对此进行一次尝试:

gcc是对的。

[temp.deduct] p5说(强调我的):

  

从默认值推导或获取所有模板参数时   模板参数,全部使用模板中的模板参数   模板的参数列表和功能类型将替换为   相应的推导或默认参数值。如果   如上所述,替换导致无效类型,类型   扣除失败。

T中使用typename enable_if<!trait<T>::value>::type...是在模板的模板参数列表中使用模板参数;由于上面的段落说所有这样的用法被替换为相应的参数值,所以需要执行此替换,尽管事实上没有模板参数绑定到此参数包。