user2962533
user2962533

Reputation: 396

std::enable_if not compiling (invalid template argument)

I'm writing some code that should accept an std::unique_ptr and activate operator>> on its contents, filling the pointer if it is empty.

I'm trying to limit unwanted calls by using SFINAE but I'm getting a bunch of error messages about template arguments being bad.

The code:

#include "serializable.h"

template <class Archive, typename T>
Archive &operator>>( Archive &in, std::unique_ptr<typename std::enable_if<typename std::is_base_of<Serializable, T>::value, T>::type> &ptr )
{
    if ( !ptr )
        ptr.swap( Serializable::construct_from_stream( in ) ); // Constructs a Serializable derivative.

    in >> *ptr;
    return in;
}

The errors:

// 1
error: template argument 1 is invalid
 Archive &operator>>( Archive &in, std::unique_ptr<typename std::enable_if<typename std::is_base_of<Serializable, T>::value, T>::type> &ptr )
                                                                                                                                     ^
// 2
error: template argument 2 is invalid

// 3
error: expected '::' before '&' token
 Archive &operator>>( Archive &in, std::unique_ptr<typename std::enable_if<typename std::is_base_of<Serializable, T>::value, T>::type> &ptr )
                                                                                                                                       ^
// 4
error: expected identifier before '&' token

// 5
error: request for member 'swap' in 'ptr', which is of non-class type 'int'
         ptr.swap( Serializable::construct_from_stream( in ) );
             ^
// 6
error: type/value mismatch at argument 1 in template parameter list for 'template<bool <anonymous>, class _Tp> struct std::enable_if'
     std::unique_ptr<typename std::enable_if<typename std::is_base_of<Serializable, T>::value, Serializable>::type>
                                                                                                           ^
// 7
error: 'Serializable' has not been declared
     ptr.swap( Serializable::construct_from_stream( in ) );
               ^

"enable_if_t" doesn't work. But I think it's just an extension of what I did wrong to make even enable_if not work in the first place.

I can tell something is very wrong because I also get an error message about operator* being applied to an int... which shouldn't have slipped inside at all (had SFINAE been implemented correctly)!

Another interesting issue here is the fact that the compiler doesn't recognize Serializable even though it's included just above it... If the answer is not trivial I'll look it up separately.

I'm compiling using MinGW 4.9.2 x32 on QtCreator 3.4.2 / Qt 5.5.0.

Thank you.

EDIT: Please do not suggest to just create a function like this:

Archive &operator>>( Archive &in, std::unique_ptr<Serializable> &ptr)...

I must know the actual type of the object sent to this function and can't rely on polymorphism.

Upvotes: 0

Views: 1987

Answers (2)

Guillaume Racicot
Guillaume Racicot

Reputation: 41780

The best way and the less intrusive one to do SFINAE is to use a non type template parameter:

template <class Archive, typename T,
    std::enable_if_t<std::is_base_of<Serializable, T>::value, int> = 0
>
Archive &operator>>( Archive &in, std::unique_ptr<T>& ptr)
{
    // ...
}

And if you want to make it shorter:

template<typename Cond>
using my_enable = std::enable_if_t<Cond::value, int>;

template<typename T>
using is_serializable = std::is_base_of<Serializable, T>;

template <class Archive, typename T, my_enable<is_serializable<T>> = 0>
Archive &operator>>( Archive &in, std::unique_ptr<T>& ptr)
{
    // ...
}

It should be way less intrusive and cause less problem with type deduction.

Upvotes: 1

cpplearner
cpplearner

Reputation: 15878

remove the typename before std::is_base_of<Serializable, T>::value (because std:is_base_of<...>::value is not a type) and move the enable_if part out of the parameter type (otherwise T will not be deducible).

template <class Archive, typename T>
typename std::enable_if<
    std::is_base_of<Serializable, T>::value,
    Archive &
>::type
  operator>>( Archive &in, std::unique_ptr<T> &ptr )

Upvotes: 3

Related Questions