为什么这个 SFINAE 失败了?
Why this SFINAE fails?
struct BLA
{
};
template<typename T>
class DUMMY
{
public:
DUMMY() = default;
template<typename U = T, typename = void>
void someFunction()
{
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
void someFunction()
{
std::cout << "someFunction()\n";
}
};
int main()
{
DUMMY<BLA> dummy;
dummy.someFunction();
}
为什么这个 SFINAE 代码调用了显示 "someFunction() - DEFAULT" 的 someFunction()?它应该调用另一个。很明显std::is_same::value是真的
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
将导致(正确替换)template<typename U = T, void>
,这是无效的。
您可能会更改为
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>
但是,这两个功能都是可行的,所以模棱两可。
所以,你终于可以做到了
template<typename U = T, typename std::enable_if<!std::is_same<U, BLA>::value, int>::type = 0>
void someFunction()
{
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>
void someFunction()
{
std::cout << "someFunction()\n";
}
在 C++17 中,更简单
void someFunction()
{
if constexpr (std::is_same<U, BLA>::value) {
std::cout << "someFunction()\n";
} else {
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
}
我正在写新答案,因为它不适合发表评论。除了@Jarod42。
你似乎认为
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
替代
template<typename U = T, typename = void>
但事实并非如此。它替代
template<typename U = T, void>
。
因此,您应该将其声明为
template<typename U = T, typename = typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
.
因为你用的typename
是用来指定依赖类型的,不是用来声明模板参数的。但是无论如何都不会像您预期的那样工作。一个默默地删除了格式错误的,另一个导致同一函数的多次声明。
大家评论后,我尽量解释的详细一些
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
void someFunction()
{
std::cout << "someFunction()\n";
}
如果T == BLA
,U
变为BLA
并使std::is_same< U , BLA>::value
true
。所以结果看起来像这样
template<typename U = BLA, void>
如果 T == NotBlaType
,U
变为 NotBlaType
并使 std::is_same<U,BLA>::value
false
。结果替换失败,std::enable_if<false,void>
没有type
.
但是在这两种情况下,都没有声明函数。因为 void
不能被允许为 non-type template parameter.
但是如果我们把void
改成int
,那就可以了。这就是为什么 @Jarod42 建议 int
.
template<void>
不合法。
但是template<int = 2>
是合法的。
使声明有效后,您应该有条件地切换函数的声明(因为两个函数具有相同的签名,所以导致多次声明)。这就是为什么 @Jarod42 的答案中的两个函数都有 std::enable_if
,它们相互求反。
struct BLA
{
};
template<typename T>
class DUMMY
{
public:
DUMMY() = default;
template<typename U = T, typename = void>
void someFunction()
{
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
void someFunction()
{
std::cout << "someFunction()\n";
}
};
int main()
{
DUMMY<BLA> dummy;
dummy.someFunction();
}
为什么这个 SFINAE 代码调用了显示 "someFunction() - DEFAULT" 的 someFunction()?它应该调用另一个。很明显std::is_same::value是真的
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
将导致(正确替换)template<typename U = T, void>
,这是无效的。
您可能会更改为
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>
但是,这两个功能都是可行的,所以模棱两可。
所以,你终于可以做到了
template<typename U = T, typename std::enable_if<!std::is_same<U, BLA>::value, int>::type = 0>
void someFunction()
{
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>
void someFunction()
{
std::cout << "someFunction()\n";
}
在 C++17 中,更简单
void someFunction()
{
if constexpr (std::is_same<U, BLA>::value) {
std::cout << "someFunction()\n";
} else {
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
}
我正在写新答案,因为它不适合发表评论。除了@Jarod42。
你似乎认为
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
替代
template<typename U = T, typename = void>
但事实并非如此。它替代
template<typename U = T, void>
。
因此,您应该将其声明为
template<typename U = T, typename = typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
.
因为你用的typename
是用来指定依赖类型的,不是用来声明模板参数的。但是无论如何都不会像您预期的那样工作。一个默默地删除了格式错误的,另一个导致同一函数的多次声明。
大家评论后,我尽量解释的详细一些
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
void someFunction()
{
std::cout << "someFunction()\n";
}
如果T == BLA
,U
变为BLA
并使std::is_same< U , BLA>::value
true
。所以结果看起来像这样
template<typename U = BLA, void>
如果 T == NotBlaType
,U
变为 NotBlaType
并使 std::is_same<U,BLA>::value
false
。结果替换失败,std::enable_if<false,void>
没有type
.
但是在这两种情况下,都没有声明函数。因为 void
不能被允许为 non-type template parameter.
但是如果我们把void
改成int
,那就可以了。这就是为什么 @Jarod42 建议 int
.
template<void>
不合法。
但是template<int = 2>
是合法的。
使声明有效后,您应该有条件地切换函数的声明(因为两个函数具有相同的签名,所以导致多次声明)。这就是为什么 @Jarod42 的答案中的两个函数都有 std::enable_if
,它们相互求反。