简体   繁体   中英

Enable_if for template function specialization

I was playing with Decorator pattern in C++. After making things work like in a book I decided to create a template function (called 'decorate') to help me construct the decorated object based on the decorator class, concrete class and interface class.

Basic template function worked perfect. But then I decided that I would like to have also an empty implementation of decorator class, which would not do anything else but calling the concrete object method. This worked and I could stick to this design, but I wasn't very satisfied by the fact that when the empty decorator is used, I would still have a virtual call there, which in fact does nothing but calling another virtual method. I wanted to avoid such unnecessary indirection and I decided to specialize my template function so that when the empty decorator is used, it returns undecorated object (concrete object). I wanted to specialize my template function using enable_if but somehow it doesn't want to compile. I must be doing something wrong. Please help me. What is wrong with my code below?

#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;

struct IA {
    virtual ~IA() {}
    virtual void doSth() = 0;
};

struct ConcreteA : public IA
{
    void doSth() override 
    {
         cout << "concrete: doSth" << endl;
    }
};

struct LoggingDecoratorA : public IA {
    LoggingDecoratorA(unique_ptr<IA> pia) : _pia(move(pia)) {}
    void doSth() override 
    {
         cout << "calling doSth" << endl;
        _pia->doSth();
    }
    unique_ptr<IA> _pia;
};

struct EmptyDecoratorA : public IA {
    EmptyDecoratorA(unique_ptr<IA> pia) : _pia(move(pia)) {}
    void doSth() override 
    {
        _pia->doSth();
    }
    unique_ptr<IA> _pia;
};

void fun(IA* pia)
{
    pia->doSth();
}

template<typename Decorator, typename Concrete, typename Interface, typename X = enable_if_t<!is_same<Decorator, EmptyDecoratorA>::value>>
unique_ptr<Interface> decorate()
{
    return make_unique<Decorator>((make_unique<Concrete>()));
}

template<typename Decorator, typename Concrete, typename Interface, typename X = void>
unique_ptr<Interface> decorate()
{
    return make_unique<Concrete>();
}

int main()
{ 
    auto pia = decorate<LoggingDecoratorA, ConcreteA, IA>();
    fun(pia.get());
}

Try this:

template<typename Decorator, typename Concrete, typename Interface>
typename enable_if<is_same<Decorator, EmptyDecoratorA>::value, unique_ptr<Interface>>::type
decorate()
{
    return make_unique<Decorator>((make_unique<Concrete>()));
}

template<typename Decorator, typename Concrete, typename Interface>
typename enable_if<!is_same<Decorator, EmptyDecoratorA>::value, unique_ptr<Interface>>::type
decorate()
{
    return make_unique<Concrete>();
}
template<typename Decorator, typename Concrete, typename Interface,
    typename X = enable_if_t<!is_same<Decorator, EmptyDecoratorA>::value>>
unique_ptr<Interface> decorate()
{
    return make_unique<Decorator>((make_unique<Concrete>()));
}

template<typename Decorator, typename Concrete, typename Interface,
    typename X = void>
unique_ptr<Interface> decorate()
{
    return make_unique<Concrete>();
}

That is not how SFINAE work (default template type parameter is not a part of a function signature, check this question for more details).

You might change the code as follows:

template<typename Decorator, typename Concrete, typename Interface>
auto decorate() ->
    enable_if_t<!is_same<Decorator, EmptyDecoratorA>::value, unique_ptr<Interface>>
{
    return make_unique<Decorator>((make_unique<Concrete>()));
}


template<typename Decorator, typename Concrete, typename Interface>
auto decorate() ->
    enable_if_t<is_same<Decorator, EmptyDecoratorA>::value, unique_ptr<Interface>>
{
    return make_unique<Concrete>();
}

and move std::enable_if_t to the return type. Now SFINAE is applied correctly because return type is a part of a function signature.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM