使用 SFINAE 禁用类模板的非模板化成员函数的最佳方法?

Ben*_*Ben 2 c++ c++-templates

有很多类似的问题,但我找不到这个问题:给定

template <typename T>
struct S {
    int f(int x) const { return x; }
};
Run Code Online (Sandbox Code Playgroud)

我希望能够f根据T. 显而易见的事情不起作用:

#include <type_traits>

template <typename T>
struct MyProperty { static constexpr auto value = std::is_same_v<T, float>; };

template <typename T>
struct S {
    template <typename = std::enable_if_t<MyProperty<T>::value>>
    int f(int x) const { return x; }
};

int main() {
    S<int> s;
}
Run Code Online (Sandbox Code Playgroud)

我能想到的最接近的是一个假人typename U = T

    template <typename U = T, typename = std::enable_if_t<MyProperty<U>::value>>
    int f(int x) const { 
        static_assert(std::is_same_v<T, U>, "Don't provide U.");
        return x;
    }
Run Code Online (Sandbox Code Playgroud)

它可以工作https://godbolt.org/z/8qGs6P8Wd但感觉很绕。有没有更好的办法?

Ted*_*gmo 7

如果您可以使用 C++20 或更高版本,请添加约束:

template<class T>
struct S {
    int f(int x) const requires MyProperty<T>::value { 
        return x;
    }
};
Run Code Online (Sandbox Code Playgroud)

在 C++17 中,您可以像现在一样执行此操作,或者将 移至enable_if用于函数的返回类型。如果您将条件放在std::is_same_v<U, T>tooenable_if而不是 a 中static_assert,那么如果您想启用其他fs,它会对 SFINAE 更加友好。

template<class T>
struct S {
    template<class U = T>
    std::enable_if_t<std::is_same_v<U, T> && MyProperty<U>::value, int> 
    f(int x) const { 
        return x;
    }
};
Run Code Online (Sandbox Code Playgroud)

如果您的类中有许多函数并且您只想在以下情况下启用,则另一个有用的选项MyProperty::valuetrue将所有这些函数放入基类中,并使用CRTP有条件地从该类继承。

struct empty {};

template<class T>
struct float_funcs {
    T& Self() { return *static_cast<T*>(this); }
    const T& Self() const { return *static_cast<const T*>(this); }

    // put all functions depending on the MyProperty trait being true here:
    int f(int x) const {
        return x + Self().foo; // `foo` is accessible since we're a friend
    }
};

template<class T> // added helper variable
inline constexpr bool MyProperty_v = MyProperty<T>::value;

// inherit from float_funcs<S<T>> if the condition is `true` or `empty` otherwise
template<class T>
struct S : std::conditional_t<MyProperty_v<T>, float_funcs<S<T>>, empty> {
private:
    friend std::conditional_t<MyProperty_v<T>, float_funcs<S<T>>, empty>;
    int foo = 1;
};
Run Code Online (Sandbox Code Playgroud)

有了这个,您就不需要 SFINAE 了f,如果你尝试使用fwhenMyProperty<T>不满足,你会得到一个明显的编译错误。f在这种情况下甚至不以任何形式存在。

  • @Ben 当然,在哪里放置 `enable_if` 是一个品味问题。如果可能的话,我个人更喜欢不向函数添加额外的虚拟模板参数。如果您不需要 SFINAE 启用其他“f”,那么“static_assert”无疑是最佳选择。我只是把它作为一个选项。最干净的肯定是 C++20 版本。:-) (2认同)