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

时间:2018-06-22 15:58:24

标签: c++ templates function-pointers member-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

最后一行产生error: creating pointer to member function of non-class type ‘const int’即使我只实例化了Foo,也根本没有使用apply 。所以我的问题是:每当T是非类类型时,如何有效地消除第二个重载?

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

3 个答案:

答案 0 :(得分: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>;

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

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

解决方法是改用函子

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

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

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

答案 1 :(得分:4)

为了消除第二个重载,您需要将其设为模板并让SFINAE正常运行,例如。 G。像这样:

template<typename T>
struct Foo
{
    T val;

    //...

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

或者,您可以完全删除第二个重载,并使用lambda或std::bind:

#include <functional> // for std::bind

template<typename T>
struct Foo
{
    T val;

    /** apply a member function */
    template<typename U, typename FuncT>
    Foo<U> apply(FuncT&& f)
    {
        return {f(val)};
    }
};

struct SomeType
{
    int getFive() { return 5; }
};

int main()
{
    Foo<SomeType> obj;

    obj.apply<int>(std::bind(&SomeType::getFive, std::placeholders::_1));
    obj.apply<int>([](SomeType& obj) { return obj.getFive(); });
}

答案 2 :(得分:2)

  

每当T为非类类型时,如何有效地消除第二个重载?

如果您至少可以使用C ++ 11(并且如果您尝试过std::function我想可以使用它),则可以将SFINAE与std::enable_if一起使用

   template <typename U, typename V>
   typename std::enable_if<std::is_class<V>{}
                        && std::is_same<V, T>{}, Foo<U>>::type
      apply(U (V::*fun)() const)
    { return Foo<U>((val.*fun)()); }

强加T是一类。

请注意,您不能直接检查T,它是该类的模板参数,但是您必须传递V类型,即特定方法的模板类型。

但是您也可以强加TV是同一类型(&& std::is_same<V, T>{})。