模板参数中是否禁止使用 SFINAE,还是我遇到了 clang 错误?
Is SFINAE forbidden in template arguments, or did I hit a clang bug?
以下是我在实际代码中遇到的问题的简化版本。
短版:只需查看 gcc.godbolt.org 处的代码和错误/长版:继续阅读 ;)
假设我想要一个带有模板参数 setting
和方法 int func(int)
的 class,例如:
- 当
setting
是 false
时,func
return 是它的参数
- 当
setting
为 true
时,func
将其自变量加倍
最简单的方法是专门化 class 模板:
template<bool setting> struct A {
int func(x) const { return 2 * x; }
};
template<> struct A<false> {
int func(x) { return x; }
};
这种方法的问题是,如果我有一堆不依赖于 setting
的其他方法,我将不得不在两个专业化中复制粘贴它们(或继承自一个通用的基础,当没有太多相互依赖时)。
因此,我可以使用 SFINAE select 正确的方法,例如std::enable_if
。这要求方法有一个模板参数,因为替换失败必须使方法无效,而不是整个 class。据我所知,失败可能发生在以下任一情况下:
- 一个方法的参数类型
- 方法的 return 类型
- 模板参数类型
下面是使用方法参数的代码:
template<bool setting> struct B {
template<bool when=true>
int func(int x
, typename std::enable_if<when && setting>::type * u=0
)
{ return 2 * x; }
template<bool when=true>
int func(int x
, typename std::enable_if<when && !setting>::type * u=0
)
{ return x; }
};
这里是使用方法模板参数的版本:
template<bool setting> struct C {
template<bool when=true, typename std::enable_if<
when && setting
>::type...>
int func(int x) { return 2 * x; }
template<bool when=true, typename std::enable_if<
when && !setting
>::type...>
int func(int x) { return x; }
};
我更喜欢最后一个版本,因为它使方法的签名更具可读性,但这是个人品味的问题。
我的问题涉及最后一个版本:C++ 有效吗? gcc 编译得很好,但 clang 没有(用 -std=c++11
/ c++1y
/ c++1z
测试,结果相同)。 class 定义本身编译正常,但在实例化时出现错误:
int main() {
A<true> a;
B<true> b;
C<true> c;
return a.func(1) + b.func(2) + c.func(3);
}
在 gcc 5.3 中编译,但在 clang 3.7.1 中不编译:
test.cpp:30:36: error: call to member function 'func' is ambiguous
return a.func(1) + b.func(2) + c.func(3);
~~^~~~
test.cpp:20:7: note: candidate function [with when = true, = <>]
int func(int x) { return 2 * x; }
^
test.cpp:23:7: note: candidate function [with when = true, = <>]
int func(int x) { return x; }
^
1 error generated.
这是有效的 C++ 吗?接受此代码是 clang 错误还是 gcc 错误?
Is SFINAE forbidden in template arguments
有效。例如,您可以这样做:
template<bool setting> struct C {
template<bool when=true, typename std::enable_if<
when && setting
>::type* = nullptr>
int func(int x) { return 2 * x; }
template<bool when=true, typename std::enable_if<
when && !setting
>::type* = nullptr>
int func(int x) { return x; }
};
typename std::enable_if<when && !setting>::type...
的问题应该与 CWG 1558 有关。
所以你的代码在 C++17 中应该是正确的。
以下是我在实际代码中遇到的问题的简化版本。
短版:只需查看 gcc.godbolt.org 处的代码和错误/长版:继续阅读 ;)
假设我想要一个带有模板参数 setting
和方法 int func(int)
的 class,例如:
- 当
setting
是false
时,func
return 是它的参数 - 当
setting
为true
时,func
将其自变量加倍
最简单的方法是专门化 class 模板:
template<bool setting> struct A {
int func(x) const { return 2 * x; }
};
template<> struct A<false> {
int func(x) { return x; }
};
这种方法的问题是,如果我有一堆不依赖于 setting
的其他方法,我将不得不在两个专业化中复制粘贴它们(或继承自一个通用的基础,当没有太多相互依赖时)。
因此,我可以使用 SFINAE select 正确的方法,例如std::enable_if
。这要求方法有一个模板参数,因为替换失败必须使方法无效,而不是整个 class。据我所知,失败可能发生在以下任一情况下:
- 一个方法的参数类型
- 方法的 return 类型
- 模板参数类型
下面是使用方法参数的代码:
template<bool setting> struct B {
template<bool when=true>
int func(int x
, typename std::enable_if<when && setting>::type * u=0
)
{ return 2 * x; }
template<bool when=true>
int func(int x
, typename std::enable_if<when && !setting>::type * u=0
)
{ return x; }
};
这里是使用方法模板参数的版本:
template<bool setting> struct C {
template<bool when=true, typename std::enable_if<
when && setting
>::type...>
int func(int x) { return 2 * x; }
template<bool when=true, typename std::enable_if<
when && !setting
>::type...>
int func(int x) { return x; }
};
我更喜欢最后一个版本,因为它使方法的签名更具可读性,但这是个人品味的问题。
我的问题涉及最后一个版本:C++ 有效吗? gcc 编译得很好,但 clang 没有(用 -std=c++11
/ c++1y
/ c++1z
测试,结果相同)。 class 定义本身编译正常,但在实例化时出现错误:
int main() {
A<true> a;
B<true> b;
C<true> c;
return a.func(1) + b.func(2) + c.func(3);
}
在 gcc 5.3 中编译,但在 clang 3.7.1 中不编译:
test.cpp:30:36: error: call to member function 'func' is ambiguous
return a.func(1) + b.func(2) + c.func(3);
~~^~~~
test.cpp:20:7: note: candidate function [with when = true, = <>]
int func(int x) { return 2 * x; }
^
test.cpp:23:7: note: candidate function [with when = true, = <>]
int func(int x) { return x; }
^
1 error generated.
这是有效的 C++ 吗?接受此代码是 clang 错误还是 gcc 错误?
Is SFINAE forbidden in template arguments
有效。例如,您可以这样做:
template<bool setting> struct C {
template<bool when=true, typename std::enable_if<
when && setting
>::type* = nullptr>
int func(int x) { return 2 * x; }
template<bool when=true, typename std::enable_if<
when && !setting
>::type* = nullptr>
int func(int x) { return x; }
};
typename std::enable_if<when && !setting>::type...
的问题应该与 CWG 1558 有关。
所以你的代码在 C++17 中应该是正确的。