这种涉及可变模板的SFINAE技术有效吗

Is this SFINAE technique involving variadic templates valid?

本文关键字:SFINAE 技术 有效      更新时间:2023-10-16

libstdc++对std::experimental::optional的实现使用了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似乎跳过了执行该替换,也许是因为它意识到该参数包上没有绑定任何模板参数。因此,它不会遇到替换失败,第一次过载仍然可行。

谁是对的

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

gcc是对的

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

当所有模板参数都已从默认值推导或获得时模板参数,所有使用模板中模板参数的模板的参数列表和函数类型被替换为相应的推导或默认参数值。如果替换导致无效的类型,如上所述,类型扣减失败。

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