Reputation: 145
I have a simple trait struct hasMemberSerialize
that I am trying to use to determine if any given class is compatible with callSerialize()
. The struct
looks like so:
template<typename Type, typename ArchiveType>
struct hasMemberSerialize {
template<typename T, typename A>
static auto test(int) -> decltype(Serialization::access::callSerialize(std::declval<A&>(), std::declval<T&>()), std::true_type);
template<typename, typename>
static std::false_type test(...);
static const bool value = std::is_same<decltype(test<Type, ArchiveType>(0)), std::true_type>::value;
};
This compiles and runs fine, however, my hasMemberSerialize::value
is always std::false_type
. I've used a similar approach to check for non-template methods; however, the callSerialize()
method I am checking looks something like:
template<typename Archive, typename Type>
static auto callSerialize(Archive& a, Type& t) -> decltype(t.serialize(a)) {
// Implementation
}
I did some tests using std::cout
like so:
Serialization::access::callSerialize(JSON, myType);
std::cout << std::boolalpha
<< hasMemberSerialize<MyType, JSONOutputArchive>::value << std::endl;
The method call callSerialize(JSON, myType)
works as expected and serializes the type; however, hasMemberSerialize::value
prints false
. finally, myType
is a simple test class:
class MyType {
int myInt;
public:
MyType() : myInt(4) {}
template<typename Archive>
void serialize(Archive& a) {
a(myInt);
}
};
...
MyType myType;
Upvotes: 1
Views: 81
Reputation: 66200
As you have discovered, the problem was that you have to use std::true_type{}
, with ending curly brackets, at the end of decltype()
So
decltype( <other elements>, std::true_type )
is wrong, and gives an error, where
decltype( <other elements>, std::true_type{} )
// .......................................^^
works.
The point is that decltype()
return the type given an entity (a variable, a constant, etc.) or an expression of that type; so (by example) given decltype(3)
, you get int
.
If you write
decltype( std::true_type )
you ask for the type of a type, and this is wrong.
If you write
decltype( std::true_type{} )
you ask for the type of an element (std::true_type{}
) of type std::true_type
; this is correct and you get std::true_type
.
I suggest another way:
decltype( std::declval<std::true_type>() )
where std::declval()
is a standard template function (only declared, but is enough for decltype()
that return an element of the template type received.
So std::declval<std::true_type>()
is an expression of type std::true_type
and decltype()
return, obviously, std::true_type
.
In case of a type that is default constructible, you can create an entity of that type simply adding a couple of curly brackets at the end of the type name. But when a type isn't default constructible, you can't solve this why.
With std::declval()
you get an expression of given type also when that type isn't default constructible.
In case of std::true_type
you can solve in both way, but I suggest to use ever std::declval()
anyway.
Upvotes: 0
Reputation: 145
I made a very simple mistake, the line
static auto test(int) -> decltype(Serialization::access::callSerialize(std::declval<A&>(), std::declval<T&>()), std::true_type);
needs to be
static auto test(int) -> decltype(Serialization::access::callSerialize(std::declval<A&>(), std::declval<T&>()), std::true_type{});
Notice: the curly brackets after std::true_type
.
As Max66 explains in his comment:
the point is that
decltype()
return the type of an object; sodecltype(3)
isint
; when you writedecltype(std::true_type)
(that is asdecltype(int)
) you ask the type of a type; you have to ask the type of an object of typestd::true_type
, that isdecltype(std::true_type{})
or (better, IMHO)decltype(std::declval<std::true_type>())
Upvotes: 1