Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template method enable_if specialization

i have following code that does not compile. This are two functions in a template class that takes the arguments

typename std::enable_if<std::is_void<Ret>::value, Ret>::type _on_dispatched() {
    // ...
}

typename std::enable_if<!std::is_void<Ret>::value, Ret>::type _on_dispatched() {
    // ....
}

I want to have a specialization in a member method depending on what type Ret is.

Has anybody some idea?

like image 495
Philipp H. Avatar asked Aug 17 '12 08:08

Philipp H.


2 Answers

SFINAE does not work on non-template functions (member or non-member).

As Kerrek SB points out, making them non-member function templates will work. Or as Xeo points out, making them member function templates with a defaulted template argument will also work.

However, this is only working because the two std::enable_if conditions are non-overlapping. If you want to add a different overload for int (say), then you'll find that it doesn't scale as nicely. Depending on what you want to do, tag dispatching generally scales better than SFINAE with multiple alternatives that you want to dispatch on:

#include<type_traits>

template<typename Ret>
class Foo
{
public:
    void _on_dispatched()
    {
        // tag dispachting: create dummy of either std::false_type or std::true_type
        // almost guaranteed to be optimized away by a decent compiler
        helper_on_dispatched(std::is_void<Ret>()); 
    } 

private:
    void helper_on_dispatched(std::false_type)
    {
        // do stuff for non-void
    }

    void helper_on_dispatched(std::true_type)
    {
        // do stuff for void
    }
};

int main()
{
    Foo<void>()._on_dispatched();
    Foo<int>()._on_dispatched();
    return 0;
}
like image 151
TemplateRex Avatar answered Sep 28 '22 09:09

TemplateRex


SFINAE only works on templates. Your code can be made to compile with a small modification:

template <typename Ret>
typename std::enable_if<std::is_void<Ret>::value, Ret>::type _on_dispatched() { /*...*/ }

template <typename Ret>
typename std::enable_if<!std::is_void<Ret>::value, Ret>::type _on_dispatched() { /*...*/ }

Usage:

auto q = _on_dispatched<int>();

You can of course not deduce the return type of a function, since it is not deducible. However, you can pack this template inside another template:

template <typename T>
struct Foo
{
    // insert templates here, maybe privately so

    T bar() { return _on_dispatched<T>(); }
};
like image 20
Kerrek SB Avatar answered Sep 28 '22 08:09

Kerrek SB