我已将问题提炼到以下类,该类试图使用std::enable_if
禁用成员函数:
#include <type_traits>
int foo(int& x) {
return x;
}
template<bool enable>
struct A {
int x;
template<bool dummy=true,
typename Enabler = typename std::enable_if<dummy && enable>::type>
auto disabled_method() const -> decltype(foo(x)) {
return foo(x);
}
};
int main() {
A<false> x;
}
表达式decltype(foo(x))
中存在类型错误,即使该函数应由enable_if
禁用!
请注意,特别是在函数的返回类型中出现。例如,如果我们将decltype(foo(x))
移动到函数体,SFINAE将正常工作:
template<bool dummy=true,
typename Enabler = typename std::enable_if<dummy && enable>::type>
auto disabled_method() const -> int {
decltype(foo((X)x)) local;
return foo(x);
}
(使用decltype(foo((X)x))
作为参数类型相同)
同样,如果我将其声明为
,它将正确执行SFINAEtemplate<bool dummy=true,
typename X = const int,
typename Enabler = typename std::enable_if<dummy && enable>::type>
auto disabled_method() const -> decltype(foo((X)x)) {
return foo(x);
}
但是使用decltype(foo((const int)x))
,尽管X等于const int
,但它会出错。这似乎表明将额外的强制转换引入模板参数X
会导致它延迟替换。
但是上面的dummy
和Enabler
模板模式应该会这样做吗?
为什么会这样?
答案 0 :(得分:4)
这与“直接背景”无关。
通常,非依赖构造中的错误会导致程序格式错误的NDR(无需诊断),请参阅[temp.res]/8。实施具有广泛的余地来诊断 - 或不诊断 - 这样的问题。
在您的情况下,foo(x)
不依赖于模板参数,并且总是格式错误;因此,即使A
从未实例化,实现也可以自由诊断。
答案 1 :(得分:1)
返回类型是SFINAE流程的一部分,这就是为什么您还可以将std::enable_if
作为返回类型的一部分。但SFINAE仅在直接上下文中存在错误时才有效,但foo(x)
不是直接模板上下文的一部分,因此编译器会抛出错误。
例如,如果您将代码更改为以下
template<bool dummy=true>
std::enable_if_t<dummy && enable, decltype(foo(x))> disabled_method()
const {
return foo(x);
}
然后enable_if
有效,因为decltype(foo(x))
不是enable_if
本身的一部分,而是x
非常数
请查看此问题及其答案,了解有关直接背景的更多信息What exactly is the "immediate context" mentioned in the C++11 Standard for which SFINAE applies?
为方便起见,来自链接问题答案的引文解释了直接背景
如果考虑确定模板参数替换结果所需的所有模板和隐式定义函数,并想象它们是在替换开始之前首先生成的,那么在第一步中发生的任何错误都不在直接上下文,并导致硬错误。
在您的示例中,需要返回类型来确定模板参数替换的结果,并且与替换过程无关,因此会导致硬错误。
同样在这种情况下,我通常会发现更容易完全省略返回类型,并让编译器推导出它。它更容易使用。