【问题标题】:Are generic function wrappers in C++ possible?C++ 中的通用函数包装器可能吗?
【发布时间】:2012-05-25 15:56:40
【问题描述】:

当我在使用 decltype 时查看 boost::fusion::fused 函数包装器中的错误时,出现了这个问题。问题似乎是无效的 decltype 是编译错误,即使不会使用需要它的模板实例化,我也不知道如何解决这个问题以创建通用函数包装器。

这是我对单参数包装器的尝试:

#include <utility>
#include <type_traits>

template <class T>
typename std::add_rvalue_reference<T>::type declval();

template <class Fn, class Arg>
struct get_return_type
{
    typedef decltype(declval<Fn>()(declval<Arg>())) type;
};

template <class Fn>
struct wrapper
{
    explicit wrapper(Fn fn) : fn(fn) {}
    Fn fn;

    template <class Arg>
    typename get_return_type<Fn,Arg&&>::type
        operator()(Arg&& arg)
    {
        return fn(std::forward<Arg>(arg));
    }

    template <class Arg>
    typename get_return_type<const Fn,Arg&&>::type
        operator()(Arg&& arg)
    {
        return fn(std::forward<Arg>(arg));
    }
};

问题是,这不适用于非 const 版本的参数不能转换为 const 版本的参数的情况。例如:

#include <iostream>

struct x {};
struct y {};

struct foo
{
    void operator()(x) { std::cout << "void operator()(x)" << std::endl; }
    void operator()(y) const { std::cout << "void operator()(y) const" << std::endl; }
};

int main()
{
    wrapper<foo> b = wrapper<foo>(foo());
    b(x()); // fail
}

在我看来,void operator()(y) const 导致的 decltype 表达式失败应该只是导致该函数因 SFINAE 而被删除。

【问题讨论】:

  • 不应该在你的操作符中为包装器返回吗?
  • @VJovic 哎呀!谢谢,我现在已经添加了。
  • 包装器中的第二个 operator() 也应该是 const。什么编译器?错误是什么?对于 g++ 4.6.1,我得到一些奇怪的错误:no match for call to (const foo)(x)
  • 我已经用 g++ 4.7 和 VS2010 试过这个。两者都以相同的方式失败 - 模板实例化 get_return_type&lt;const Fn,Arg&amp;&amp;&gt;::type 失败,而不是由于 SFINAE 而被删除,而是导致编译错误。

标签: c++ functional-programming generic-programming decltype


【解决方案1】:

这是在 g++ 4.6.1 上编译良好的代码:

#include <utility>
#include <type_traits>
#include <iostream>

template <class Fn, class Arg>
struct get_return_type
{
    typedef decltype( std::declval<Fn>() ( std::declval<Arg>() ) ) type;
};

template <class Fn>
struct wrapper
{
    explicit wrapper(Fn fn) : fn(fn) {}
    Fn fn;

    template <class Arg>
    typename get_return_type<Fn,Arg&&>::type
    operator()(Arg&& arg)
    {
        return fn(std::forward<Arg>(arg));
    }

    template <class Arg>
    typename get_return_type< const Fn,Arg&&>::type
    operator()(Arg&& arg) const
    {
        return fn(std::forward<Arg>(arg));
    }
};

struct x {};
struct y {};

struct foo
{
    x* operator()(x) { std::cout << "x* operator()(x)" << std::endl; return nullptr; }
    x operator()(x) const { std::cout << "x operator()(x) const" << std::endl; return x(); }
    y* operator()(y) { std::cout << "y* operator()(y)" << std::endl; return nullptr; }
    y operator()(y) const { std::cout << "y operator()(y) const" << std::endl; return y(); }
};

template <class Fn>
void test_foo(Fn fn)
{
    // make sure all operator() overloads are callable
    const Fn& cfn = fn;

    x* a = fn(x());
    x b = cfn(x());
    y* c = fn(y());
    y d = cfn(y());
(void)a;(void)b;(void)c;(void)d;
}

int main()
{
    test_foo(foo());
    test_foo(wrapper<foo>(foo())); // fail
}

【讨论】:

  • 好吧,如果你改变函子,那当然可以,但关键是可以编写一个可以包装每个函数的函数包装器吗?
  • @Ayjay 上面的代码是固定版本,可以很好地编译 x 和 y :)
  • 但是现在它不能正确确定const函子的返回类型。
  • @Ayjay 期望什么?你得到了什么?
  • @BЈовић 我希望包装类能够包装任何包含任意数量的 operator() 重载的函数对象。只要被调用者调用了有效的重载,它就应该工作。
猜你喜欢
  • 1970-01-01
  • 2019-08-03
  • 2011-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多