根据lambda的结果过载

Bra*_*don 3 c++ lambda c++11

我有一个类:

class foo
{
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;

    template<typename T>
    void addListener(int e, T&& lambda) { events.add(e, lambda); }
};
Run Code Online (Sandbox Code Playgroud)

但是,我需要接受两种类型的lambda.一个有签名:

[&](UINT, WPARAM, LPARAM) -> LRESULT {}和一个签名:[&](UINT, WPARAM, LPARAM) -> void {}.

所以我想确定lambdas的返回类型..

我尝试过类似的东西:

template<typename T>
void addListener(int e, T&& listener)
{
    if (std::is_void<decltype(listener(0, 0, 0))>::value)
    {
        Events.Subscribe(e, [&](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    Events.Subscribe(e, [&](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
        std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
        return func();
    });
}
Run Code Online (Sandbox Code Playgroud)

我还尝试将该函数声明为具有以下签名的模板:

template<typename T, typename = typename std::enable_if<std::is_void<typename std::result_of<T(UINT, WPARAM, LPARAM)>::type>::value>::type>

超载它但它也不起作用..

任何想法我可以做什么而不必创建两个方法(每个签名一个)?

Pio*_*cki 5

选项1

将呼叫分配到适当的过载:

#include <functional>
#include <type_traits>
#include <utility>

class foo
{
public:
    template <typename T>
    void addListener(int e, T&& listener)
    {
        addListener(e, std::forward<T>(listener), std::is_void<decltype(listener(0, 0, 0))>{});
    }

private:
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;

    template <typename T>
    void addListener(int e, T&& listener, std::true_type)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    template <typename T>
    void addListener(int e, T&& listener, std::false_type)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
            return func();
        });
    }
};
Run Code Online (Sandbox Code Playgroud)

演示1

选项#2

使用SFINAE条件添加重载(typename = void如果要enable_if在第二个重载中隐藏模板参数列表,则需要一个虚拟类型模板参数,以使函数模板的声明不同):

#include <functional>
#include <type_traits>

class foo
{
public:    
    template <typename T,
              typename = typename std::enable_if<std::is_void<typename std::result_of<T(UINT, WPARAM, LPARAM)>::type>::value>::type>
    void addListener(int e, T&& listener)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    template <typename T,
              typename = typename std::enable_if<!std::is_void<typename std::result_of<T(UINT, WPARAM, LPARAM)>::type>::value>::type,
              typename = void>
    void addListener(int e, T&& listener)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
            return func();
        });
    }

private:
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;
};
Run Code Online (Sandbox Code Playgroud)

演示2

选项#3

使用带有的尾随返回类型 decltype()说明符来启用/禁用重载:

#include <functional>
#include <type_traits>
#include <utility>

class foo
{
public:    
    template <typename T>
    auto addListener(int e, T&& listener)
        -> typename std::enable_if<std::is_void<decltype(std::forward<T>(listener)(0, 0, 0))>{}>::type
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    template <typename T>
    auto addListener(int e, T&& listener)
        -> typename std::enable_if<!std::is_void<decltype(std::forward<T>(listener)(0, 0, 0))>{}>::type
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
            return func();
        });
    }

private:
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;
};
Run Code Online (Sandbox Code Playgroud)

演示3