SFINAE启用非模板成员功能

时间:2014-03-26 19:38:03

标签: c++ templates sfinae

这可能是重复的,但我找不到一个OP显然有同样的问题。
我有一个类,我只是在类模板参数不是无符号类型时才尝试启用operator-

#include <type_traits>

template<class T>
struct A {
    typename std::enable_if<!std::is_unsigned<T>::value,A>::type operator-() {return {};}
};

int main() {
    A<unsigned> a=a;
}

不幸的是,只要我用无符号类型实例化它就会产生编译器错误,如图所示。

main.cpp:5:29: error: no type named 'type' in 'std::enable_if<false, A<unsigned int> >'; 'enable_if' cannot be used to disable this declaration
    typename std::enable_if<!std::is_unsigned<T>::value,A>::type operator-() {return {};}
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:9:17: note: in instantiation of template class 'A<unsigned int>' requested here
    A<unsigned> a=a;
                ^

好吧,我可以清楚地看到enable_if在这里不起作用。一个模糊的类似问题提到我可以使用继承和模板特化来解决这个问题,但是......真的没有更好的方法吗?

4 个答案:

答案 0 :(得分:4)

我曾经遇到过同样的问题。事实证明,由于默认模板参数不依赖于函数模板中的模板参数,因此无法进行替换失败。您必须将模板参数默认为封闭模板类型,如下所示:

template<typename U = T,
         class = typename std::enable_if<!std::is_unsigned<U>::value, U>::type>
A operator-() { return {}; }

答案 1 :(得分:4)

评论有点长:你也可以使用免费功能,即使对于一元运算符也是如此。

#include <type_traits>

template<class T>
struct A {
};

template<class T>
typename std::enable_if<!std::is_unsigned<T>::value,A<T>>::type
operator-(A<T>) {return {};}

int main() {
    A<signed> b;
    -b; // ok

    A<unsigned> a;
    -a; // error
}

这并没有为每个类模板引入成员函数模板。


以下是你如何与它交朋友:

template<class T>
class A {
    int m;

public:
    A(T p) : m(p) {}

    template<class U>
    friend
    typename std::enable_if<!std::is_unsigned<U>::value,A<U>>::type
    operator-(A<U>);
};

template<class T>
typename std::enable_if<!std::is_unsigned<T>::value,A<T>>::type
operator-(A<T> p) {return {p.m};}

int main() {
    A<signed> b(42);
    -b; // ok

    A<unsigned> a(42);
    //-a; // error
}

你可以(应该)正向声明该功能模板。

答案 2 :(得分:3)

一种可能的方法是引入虚拟模板参数:

template<class T>
struct A {
    template<
       typename D = int,
       typename = typename std::enable_if<!std::is_unsigned<T>::value, D>::type
    >
    A operator-() {return {};}
};

答案 3 :(得分:1)

使用继承还有很长的路要走:

template <class T>
struct A;

template <class T, bool = std::is_unsigned<T>::value>
struct MinusOperator
{
    A<T> operator-()
    {
        A<T>* thisA = static_cast<A<T>*>(this);
        // use thisA instead of this to get access to members of A<T>
    }
};

template <class T>
struct MinusOperator<T, true> {};

template <class T>
struct A : MinusOperator<T>
{
    friend struct MinusOperator<T>;
};