Question

Lets consider the following class which checks if the type provided is a string and uses type traits to call a different function based on the result. The original source has type dependencies on Obj, this is a simplified example:

template <typename Obj> 
class example {
    public:            
        template <bool IdIsString = std::is_same<std::string, Obj>::value>
        typename std::enable_if<IdIsString, void>::type
        doStuff() {
            this->object->doSomething();
        }

        template <bool IdIsString = std::is_same<std::string, Obj>::value>
        typename std::enable_if<!IdIsString, void>::type
        doStuff()  {
            this->object->doSomethingElse();
        }
     private:
        AnyObject object;
};

How would I go about decoupling the definition (e.g. storing it in a example_inline.hpp) from the class without providing each type trait case in the definition?

An ideal solution would look like this:

// header.hpp
template <typename Obj> 
class example {
    public:
        void doStuff(); 
}

// header_inline.hpp
template <typename Obj>
template <bool IdIsString = std::is_same<std::string, Obj>::value>
typename std::enable_if<IdIsString, void>::type
example::doStuff() {
   // ...
}

// ...

The above is obviously not possible. One solution would be to decouple the type_trait function from the class and put it into a detail namespace but this would mean that I would have to always pass AnyObject (and in that sense, all objects that are modified by the function) to it which seems not very elegant.

Is there a good solution for this problem? I'd really like for the headers in question be easily readable without cluttering them with tons of enable_if's.

Thanks for any input on the matter.

Was it helpful?

Solution

You can overload based on the return value of std::is_same:

template<typename T>
class example
{
private:
    void doStuff(std::true_type)
    {
        obj->doSomething();
    }

    void doStuff(std::false_type)
    {
        obj->doSomethingElse();
    }
public:
    void doStuff()
    {
        doStuff(std::is_same<T, std::string>{});
    }
};

OTHER TIPS

In this simple case, you could also specialize doStuff on the template parameter of the class template:

#include <iostream>
#include <string>

template<class T>
struct example
{ void doStuff(); };

template<class T>
void example<T>::doStuff()
{ std::cout<<"default\n"; }

template<>
void example<std::string>::doStuff()
{ std::cout<<"string\n"; }

int main()
{
    example<int>{}.doStuff();
    example<std::string>{}.doStuff();
}

Live example

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top