I am having trouble using the SFINAE concept to detect if a class has a particular template method with some compilers, most pertinently GCC. Consider the code below. It compiles and works as expected with MSVC; however, when I compile the same code with GCC, it complains that std::vector<double> does not have a serialize method. Of course, the fact that the method does not exist is true, but I expected this to result in a substitution failure and the compiler determining that the other less specialized fallback is most suitable. Am I missing something about SFINAE or is this a bug with GCC?
#include <iostream>
#include <utility>
#include <vector>
class SerializerBase
{
public:
SerializerBase() {}
};
template<typename T>
struct has_serialize
{
template<typename C>
static constexpr auto test(...) -> std::false_type;
/// Note: this is where the compiler complains about the .serialize method
/// during substitution.
template<typename C>
static constexpr auto test(int)
-> decltype(std::declval<T>().serialize(std::declval<SerializerBase&>()), std::true_type());
using result_type = decltype(test<T>(0));
static const bool value = result_type::value;
};
class Serializer : public SerializerBase
{
public:
Serializer() {}
template<typename T,
typename std::enable_if<!has_serialize<T>::value, T>::type* = nullptr>
void operator()(const T& v)
{
std::cout << "fallback called" << std::endl;
}
template<typename T,
typename std::enable_if<has_serialize<T>::value, T>::type* = nullptr>
void operator()(const T& v)
{
v.serialize(*this);
}
};
struct Foo
{
template<typename SerializerType>
void serialize(SerializerType& s) const
{
std::cout << "serialize called" << std::endl;
}
};
int main()
{
Serializer s;
std::vector<double> v;
Foo f;
s(v);
s(f);
return 0;
}