尝试禁用no-args成员函数时,SFINAE在decltype()内部不起作用

时间:2017-06-22 03:41:53

标签: c++ template-meta-programming sfinae enable-if

我已将问题提炼到以下类,该类试图使用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))作为参数类型相同)

同样,如果我将其声明为

,它将正确执行SFINAE
template<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会导致它延迟替换。

但是上面的dummyEnabler模板模式应该会这样做吗?

为什么会这样?

2 个答案:

答案 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?

为方便起见,来自链接问题答案的引文解释了直接背景

  

如果考虑确定模板参数替换结果所需的所有模板和隐式定义函数,并想象它们是在替换开始之前首先生成的,那么在第一步中发生的任何错误都不在直接上下文,并导致硬错误。

在您的示例中,需要返回类型来确定模板参数替换的结果,并且与替换过程无关,因此会导致硬错误。

同样在这种情况下,我通常会发现更容易完全省略返回类型,并让编译器推导出它。它更容易使用。