避免非类类型的指向成员函数

Sim*_*mon 4 c++ templates member-function-pointers function-pointers

我正在编写一种容器类,我想提供一种apply方法来评估容器内容的函数.

template<typename T>
struct Foo
{
    T val;

    /** apply a free function */
    template<typename U> Foo<U> apply(U(*fun)(const T&))
    {
        return Foo<U>(fun(val));
    }

    /** apply a member function */
    template<typename U> Foo<U> apply(U (T::*fun)() const)
    {
        return Foo<U>((val.*fun)());
    }
};

struct Bar{};
template class Foo<Bar>; // this compiles
//template class Foo<int>; // this produces an error
Run Code Online (Sandbox Code Playgroud)

最后一行产生error: creating pointer to member function of non-class type ‘const int’.虽然我只实例化Foo,而不是用apply在所有.所以我的问题是:如果T是非类型类型,我怎样才能有效地删除第二个重载?

注意:我也试过只有一个重载std::function<U(const T&)>.这种方法有效,因为函数指针和成员函数指针都可以转换为std::function,但这种方法有效地禁用了模板推导,U这使得用户代码的可读性降低.

Pas*_* By 6

使用std::invoke相反的帮助,实现和阅读更容易

template<typename T>
struct Foo
{
    T val;

    template<typename U> auto apply(U&& fun)
    {
        return Foo<std::invoke_result_t<U, T>>{std::invoke(std::forward<U>(fun), val)};
    }
};

struct Bar{};
template class Foo<Bar>;
template class Foo<int>;
Run Code Online (Sandbox Code Playgroud)

但是,如果函数重载,则无法编译

int f();
double f(const Bar&);
Foo<Bar>{}.apply(f);  // Doesn't compile
Run Code Online (Sandbox Code Playgroud)

解决这个问题的方法是使用仿函数

Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });
Run Code Online (Sandbox Code Playgroud)

这也使其与成员函数调用更加一致

Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });
Run Code Online (Sandbox Code Playgroud)