Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SFINAE gives different results on GCC and Clang

I'm learning how to use SFINAE to my advantage. I'm trying to use it to select the function implementation based on existence of a serialize() function in an object.

This is the code I use to determine, if the type defines the serialize() function:

template <typename T>
class HasSerialize {
    private:
        typedef char yes[1];
        typedef char no[2];

        template <typename C> static yes& test(char[sizeof(&C::serialize)]) ;
        template <typename C> static no& test(...);
    public:
        static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

However, it seems to give exactly oposite results on GCC and on Clang. Assume the following code:

template<bool T>
class NVPtypeSerializer {
    public:
        template<typename C>
        static xmlChar* serialize(C value) {
            // serize() is not available
        }
};

template<>
struct NVPtypeSerializer<true> {
    public:
        template<typename T>
        static xmlChar* serialize(T value) {
            return value.serialize();
        }
};

Which is called like this:

foo = NVPtypeSerializer<HasSerialize<Bar>::value >::serialize(value);

Where the class Bar doesn't have the serialize() function. This code compiles fine under Clang 3.1, however on GCC 4.7.1 I get the following errors:

error: ‘class Bar’ has no member named ‘serialize’

If I change the struct NVPtypeSerializer<true> to struct NVPtypeSerializer<false> it can be compiled on GCC, but Clang gives the following error:

error: no member named 'serialize' in 'Bar'

Where is the problem? Is it in my code? I'd like to have the code portable as much as possible.

like image 330
stativ Avatar asked Jul 19 '12 17:07

stativ


1 Answers

Is this really the code test(char[sizeof(&C::serialize)])? Note that a declaration of a function that takes an array actually declares a function that takes a pointer:

template <typename C> static yes& test(char[sizeof(&C::serialize)]) ;

That actually means:

template <typename C> static yes& test( char* );

Which incidentally is what makes your call test<C>(0) compile. I don't think that is the proper way of detecting whether the function exists or not. Google on how to detect whether a member/member function exists in a class using SFINAE.

(A simple solution would be adding an extra defaulted argument --provided that you have a C++11 enabled compiler:

template <typename C, std::size_t = sizeof(&C::serialize)> 
static yes& test(int) ;

)

like image 169
David Rodríguez - dribeas Avatar answered Oct 26 '22 19:10

David Rodríguez - dribeas