Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional enable an alternative assignment operator

I'm trying to conditionally instantiate an extra assignment operator. The code below works fine in clang, but not in gcc 4.7.

The problem I'm having seems very similar the the question asked here: std::enable_if to conditionally compile a member function

The following illustrates the problem I'm having:

#include <type_traits>

template<typename T>
struct StrangerTypeRules;

template<typename T>
struct X;

template< >
struct StrangerTypeRules < unsigned > {
    typedef unsigned type;
};

template< >
struct StrangerTypeRules < bool > {
    typedef X<bool> type;
};

template<typename T>
struct X {
    // In the non-trivial version of my code, I can not use the
    // default assignment operator, therefor I need to define this one
    X& operator=( const X<T>& rhs ) {
        return *this;
    }

    // Alternative assignment oprtator, must only exists if it is
    // different from the assignment operator above
    template<typename =
        typename std::enable_if<
            ( !std::is_same<
                X<T>,
                typename StrangerTypeRules<T>::type
            >::value ),
            X<T>
        >::type
    >
    X<T> & operator=( const typename StrangerTypeRules <T>::type& rhs ) {
        return *this;
    }
};

int main(int argc, const char *argv[])
{
    X<unsigned> x1, x2;

    x1 = 4;
    x2 = x1;

    X<bool> x3, x4; // compile error in gcc 4.7 with -std=c++11
    //x3 = x4;

    return 0;
}

Can this be done in a way which satisfies both clang and gcc 4.7? If so, how?

Compilations error when using gcc:

test.cxx: In instantiation of ‘struct X<bool>’:
test.cxx:52:13:   required from here
test.cxx:38:12: error: no type named ‘type’ in ‘struct std::enable_if<false, X<bool> >’
like image 608
Allan Avatar asked Nov 05 '22 03:11

Allan


1 Answers

You need to make the enable_if dependent on a template parameter. As it is now, it is only dependent on the outer template parameter within the template definition. But if you instantiate a class from the outer template, then your assignment operator template that is instantiated into that class is not dependent on a template parameter anymore because T will have been substituted already.

Just introduce a dummy parameter equal to T

template<typename T1 = T, typename =
    typename std::enable_if<
        ( !std::is_same<
            X<T1>,
            typename StrangerTypeRules<T1>::type
        >::value ),
        X<T1>
    >::type
>
X<T1> & operator=( const typename StrangerTypeRules <T1>::type& rhs ) {
    return *this;
}

Using T1 in just one of theplaces within the enable_if<...> template arguments would suffice already, because that already makes the enable_if dependent.

However it is not the call to your operator= that was previously illformed but the declaration of it, which conflicts with the copy-assignment operator, so the enable_if has little utility here. Just replace your code by

template<typename T1 = T>
X<T1> & operator=( const typename StrangerTypeRules <T1>::type& rhs ) {
    return *this;
}

Since this operator= is a template, it will not conflict with the non-template overload. And also because it is a template, when you call it the compiler will prefer the non-template if T is X<T>.

like image 194
Johannes Schaub - litb Avatar answered Nov 09 '22 07:11

Johannes Schaub - litb