在虚函数上使用enable_if

5
#include <type_traits>

class Base {
public:
    virtual bool f() {
        return true;
    }
};

template<typename T>
class Derived : public Base {
    std::enable_if_t< std::is_copy_constructible<T>::value, bool > f() override {
        return true;
    }

    std::enable_if_t< !std::is_copy_constructible<T>::value, bool > f() override {
        return false;
    }
};

以上代码无法编译。由于某些原因,我无法理解的是,在SFINAE移除一个函数之前,编译器将这两个函数视为同一个重载。

然而,我不明白的是如何解决这个问题。我找到的文档state表示应该对该函数进行模板化。然而,这并不起作用,因为该函数应该是虚拟的。

我尝试通过调用非虚拟函数来解决这个问题,但我也无法使其编译:

template<typename T>
class Derived : public Base {
    virtual bool f() override {
        return f_impl();
    }

private:
    template< std::enable_if_t< std::is_copy_constructible<T>::value > = 0 >
    bool f_impl() {
        return true;
    }

    template< std::enable_if_t< !std::is_copy_constructible<T>::value > >
    bool f_impl() {
        return false;
    }
};

int main() {
    Derived<int> a;

    std::cout<<a.f()<<"\n";
}

这将导致编译失败:

so.cpp: In instantiation of ‘class Derived<int>’:
so.cpp:29:18:   required from here
so.cpp:18:10: error: ‘std::enable_if<true, void>::type’ {aka ‘void’} is not a valid type for a template non-type parameter

我显然做错了什么,但我无法弄清楚正确的方法是什么。


2
为什么不直接返回一个值 bool f() override { return std::is_copy_constructible<T>::value; } - user7860670
2
因为这只是一个简化的例子。实际上,我需要做两件非常不同的事情。 - Shachar Shemesh
2个回答

7

很遗憾,您无法这样做。SFINAE 只适用于模板。例如,从您的第二个示例修改的以下代码可以工作。

template< typename X = T>
std::enable_if_t< std::is_copy_constructible<X>::value, bool >
f_impl() {
    return true;
}

template< typename X = T>
std::enable_if_t< !std::is_copy_constructible<X>::value, bool >
f_impl() {
    return false;
}

进入链接

但是虚函数不能是模板,仅此而已。


你能详细说明一下为什么使用 std::is_copy_constructible<T> 的这个解决方案不起作用吗? - Shachar Shemesh
@ShacharShemesh std::enable_if 应该依赖于函数模板本身的模板参数。否则,SFINAE 将无法工作;f_impl 两者都将被实例化,而任何一个都肯定是不合法的。 - songyuanyao

4
使用if constexpr,可以在编译时在函数内进行分支操作,因此函数可以保持virtual
bool f() override
{
    if constexpr(std::is_copy_constructible<T>::value)
    {
        return true;
    }
    else
    {
        return false;
    }
}

我可以这样做吗 if constexpr(false) nonExstingFunction();?因为这只是一个简化的例子。 - Shachar Shemesh
@ShacharShemesh 不,两个分支中使用的所有名称都必须引用某些已知实体。对于模板也是如此。即使未实例化,模板也不允许引用不存在的函数和变量。在if constexpr(std::is_copy_constructible<T>::value)的真分支中可以使用T类型的复制构造函数,而不会触发错误,即使它实际上不可用。 - user7860670
所以我可以调用一个已存在函数的不存在重载版本,但不能调用完全未声明的函数。不幸的是,这离真正的static_if还有很大差距。 - Shachar Shemesh
@JVApen static_assert 的参数也不需要依赖于模板参数。但是 static_assert(false) 会使程序无效。 - user7860670
@JVApen 这里的用例根本无法很好地转换为 #ifdef。条件在编译时已知,而不是在预处理期间。 - Shachar Shemesh
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接