jeudi 29 mars 2018

Hide class template instance based on traits

I have a traits class like the following that reflects the compatibility between two types:

template <typename ObjectType, typename ArgumentType>
struct Traits
{
    static const bool SpecialMethodAvailable = false;
};  

The single member determines if SpecialMethod() can be called on objects of type ObjectType with argument of type ArgumentType.

A simple class that supports this is the following:

class ClassWithSpecialMethod
{
public:
    template <typename T>
    void SpecialMethod(T param) { std::cout << "Special Method called with " << param << std::endl; }
};

template <typename ArgumentType>
struct Traits<ClassWithSpecialMethod, ArgumentType>
{
    static const bool SpecialMethodAvailable = true;
};

I want to write a worker class that uses this traits class and calls the special method if it is available. Basically something like the following:

template <typename T>
struct Worker
{
    static void DoSomething(T t, GlobalDataType& globalData)
    {
        //if Traits<GlobalDataType, T>::SpecialMethodAvailable
        //    call the method
        //else
        //    do something different
    }
};

I tried to realize this using std::enable_if. My solution works with the Visual C 14.1 compiler but not with GCC. Here is what I tried:

template <typename T, typename Enable = void>
struct Worker
{
    static void DoSomething(T t, GlobalDataType& globalData)
    {
        std::cout << "There is no special method (called with " << t << ")" << std::endl;
    }
};

template <typename T>
struct Worker<T, typename std::enable_if<Traits<GlobalDataType, T>::SpecialMethodAvailable>::type>
{
    static void DoSomething(T t, GlobalDataType& globalData)
    {
        globalData.SpecialMethod(t);
    }
};

I used this as follows:

typedef ... GlobalDataType; //before the template declarations

int main()
{
    GlobalDataType td;

    int integer = 0;
    Worker<int>::DoSomething(integer, td);
}

If GlobalDataType is typedef'ed to ClassWithSpecialMethod, both VS and GCC compile fine and output correctly:

Special Method called with 0

However, if GlobalDataType is typedef'ed to something that does not allow the special method (e.g. int), VS still produces the correct output while GCC results in a compile error:

In static member function ‘static void Worker::SpecialMethodAvailable>::type>::DoSomething(T, GlobalDataType&)’: source.cpp:38:15: error: request for member ‘SpecialMethod’ in ‘globalData’, which is of non-class type GlobalDataType {aka int}’

Can someone explain why this does not work as intended under GCC? What would be alternatives?

Link to online compiler

Aucun commentaire:

Enregistrer un commentaire