Reputation: 396
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
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
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