草庐IT

C++11 lambda 和参数包

coder 2024-02-22 原文

我遇到的问题与 this question 基本相同,但不幸的是,唯一发布的答案现在是一个死链接。

具体来说,使用 VS2013 Update 4,我试图编译以下代码,但它不合作:

template<typename T, typename... Params>
auto PostWeakTask(const boost::weak_ptr<IWorkQueue>& queue,
                  void (T::*member)(Params...),
                  const boost::weak_ptr<T>& weak)
    -> std::function<void (Params&&...)>
{
    return [queue, member, weak](Params&&... params)
    {
        if (auto qp = queue.lock())
        {
            qp->Post([weak, member, params]()
            {
                if (auto strong = weak.lock())
                {
                    ((*strong).*member)(std::forward<Params>(params)...);
                }
            });
        }
    };
}

(正如所写,params 的捕获失败并显示为 C3481: 'params': lambda capture variable not found。如果我尝试使用 = 的隐式捕获,它会显示 C2065: 'params' : undeclared identifier。如果我尝试 params...,我会得到 C3521: 'params' is not a parameter pack。 )

当然,我们的想法是返回一个函数,该函数在被调用时会将带有任意参数的成员函数发布到接受 void() 的工作队列中。仅任务,并且仅在尚未执行时保留对队列和任务的弱引用。

不过,我不认为这里的代码有任何实际错误。我想我已经找到了使用 bind 的解决方法而不是 lambda,但奇怪的是它似乎只适用于 std::function而不是 boost::function . (使用 Boost 1.55。我怀疑这可能是 pre-rvalue-ref 支持?)

(附带说明,我最初尝试使用 decltype([](Params&&...){}) 作为返回类型,因为这看起来更自然。但它会使编译器崩溃。)


解决方法也做了一些奇怪的事情。也许我应该把这个作为一个单独的问题来问,因为它主要与完美转发部分有关,但是:

template<typename T, typename... Params>
auto PostWeakTask(const boost::weak_ptr<IWorkQueue>& queue,
                  void (T::*member)(Params...),
                  const boost::weak_ptr<T>& weak)
    -> std::function<void (Params...)>
{
    struct WeakCaller
    {
        typedef void (T::*member_type)(Params...);
        typedef boost::weak_ptr<T> weak_type;

        WeakCaller(member_type member, const weak_type& weak)
            : m_member(member), m_weak(weak) {}

        void operator()(Params&&... params)
        {
            if (auto strong = m_weak.lock())
            {
                ((*strong).*m_member)(std::forward<Params>(params)...);
            }
        }

    private:
        member_type m_member;
        weak_type m_weak;
    };

    return [=](Params&&... params)
    {
        if (auto qp = queue.lock())
        {
            qp->Post(std::bind(WeakCaller(member, weak),
                     std::forward<Params>(params)...));
        }
    }
}

这看起来应该可以工作,但是当我尝试调用它时(使用 void (tribool,tribool,const std::string&) 方法)我得到一个绑定(bind)错误 tribool参数与 tribool&& 不兼容一个。

(特别是:C2664: 'void WeakCaller<T,boost::logic::tribool,boost::logic::tribool,const std::string &>::operator ()(boost::logic::tribool &&,boost::logic::tribool &&,const std::string &)' : cannot convert argument 1 from 'boost::logic::tribool' to 'boost::logic::tribool &&' .)

我认为以这种方式使用右值引用的部分意义在于它们应该可以完美转发而不需要多次重载?

我可以通过制作 WeakCaller 来编译它有void operator()(Params... params)相反,但这不会打败完美转发吗? (奇怪的是,将 && 留在顶层 lambda 似乎没问题……而且我不确定 std::function 签名和 lambda 签名之间的不匹配是否正常。)


仅供引用,这是我现在使用的最终版本,经过调整 Oktalist's answer :

namespace detail
{
    template<size_t... Ints> struct index_sequence
    {
        static size_t size() { return sizeof...(Ints); }
    };

    template<size_t Start, typename Indices, size_t End>
    struct make_index_sequence_impl;

    template<size_t Start, size_t... Indices, size_t End>
    struct make_index_sequence_impl<Start, index_sequence<Indices...>, End>
    {
        typedef typename make_index_sequence_impl<
            Start + 1, index_sequence<Indices..., Start>, End>::type type;
    };

    template<size_t End, size_t... Indices>
    struct make_index_sequence_impl<End, index_sequence<Indices...>, End>
    {
        typedef index_sequence<Indices...> type;
    };

    template <size_t N>
    using make_index_sequence = typename
        make_index_sequence_impl<0, index_sequence<>, N>::type;

    template<typename... Ts>
    using index_sequence_for = make_index_sequence<sizeof...(Ts)>;

    template<typename... Ts>
    struct MoveTupleWrapper
    {
        MoveTupleWrapper(std::tuple<Ts...>&& tuple)
            : m_tuple(std::move(tuple)) {}
        MoveTupleWrapper(const MoveTupleWrapper& other)
            : m_tuple(std::move(other.m_tuple)) {}
        MoveTupleWrapper& operator=(const MoveTupleWrapper& other)
            { m_tuple = std::move(other.m_tuple); }

        template<typename T, typename... Params>
        void apply(void (T::*member)(Params...), T& object) const
        {
            applyHelper(member, object, index_sequence_for<Ts...>());
        }

        template<typename T, typename... Params, size_t... Is>
        void applyHelper(void (T::*member)(Params...), T& object,
                         index_sequence<Is...>) const
        {
            (object.*member)(std::move(std::get<Is>(m_tuple))...);
        }

    private:
        mutable std::tuple<Ts...> m_tuple;
    };

    template<typename... Ts>
    auto MoveTuple(Ts&&... objects)
        -> MoveTupleWrapper<std::decay_t<Ts>...>
    {
        return std::make_tuple(std::forward<Ts>(objects)...);
    }

    template<typename T, typename... Params>
    struct WeakTaskPoster
    {
        WeakTaskPoster(const boost::weak_ptr<IWorkQueue>& queue,
                       void (T::*member)(Params...),
                       const boost::weak_ptr<T>& weak) :
            m_queue(queue),
            m_member(member),
            m_weak(weak)
        {}

        template<typename... XParams>
        void operator()(XParams&&... params) const
        {
            if (auto qp = m_queue.lock())
            {
                auto weak = m_weak;
                auto member = m_member;
                auto tuple = MoveTuple(std::forward<XParams>(params)...);
                qp->Post([weak, member, tuple]()
                {
                    if (auto strong = weak.lock())
                    {
                        tuple.apply(member, *strong);
                    }
                });
            }
        }

    private:
        boost::weak_ptr<IWorkQueue> m_queue;
        void (T::*m_member)(Params...);
        boost::weak_ptr<T> m_weak;
    };
}

template<typename T, typename... Params>
auto PostWeakTask(const boost::weak_ptr<IWorkQueue>& queue,
                  void (T::*member)(Params...),
                  const boost::weak_ptr<T>& weak)
    -> detail::WeakTaskPoster<T, Params...>
{
    return { queue, member, weak };
}

我猜它仍然不是真正通用的,因为它不适用于左值引用,但由于这些在延迟回调上下文中是危险的,所以这并不是那么重要。

(尽管如此,如果您真的急于传递左值引用,那么如果您调用 std::ref(x) 并接收为 std::reference_wrapper<T>(或 const&&&),它似乎可以工作。可能还有一些额外的可以使它透明的魔法,但我真的不需要它,所以我没有调查过。)

最佳答案

I thought part of the point of using rvalue references this way was that they were supposed to forward perfectly without needing multiple overloads?

不,右值引用和转发引用是两个不同的东西。它们都涉及 &&但他们的行为不同。转发引用仅在存在模板参数推导(或 auto 类型推导,使用相同的规则)时发生。

这是转发引用:

template<typename T>
void foo(T&& v);

模板参数推导会让T是任何类型;如果T那么 v 是一个左值引用是左值引用,否则 v由于引用折叠,是右值引用。

这不是转发引用:

void foo(int&& v);

这里没有模板参数推导,所以v是对 int 的普通右值引用, 它只能绑定(bind)到右值。

这也不是转发引用:

template<typename T>
struct Foo
{
    void bar(T&& v);
};

同样是因为没有模板参数推导。函数Foo<int>::bar不是模板,它是一个普通函数,采用普通右值引用参数,只能绑定(bind)到右值。函数Foo<int&>::bar是一个普通函数,采用普通左值引用参数,只能绑定(bind)到左值。

On a side note, I originally tried to use decltype([](Params&&...){}) as the return type, as this seems more natural.

那是行不通的。每个 lambda 表达式都表示一个唯一的类型,因此 decltype([]{})decltype([]{})是两种不同的、不相关的类型。

这是一个可能的解决方案。唯一使用的 C++14 特性是 std::index_sequence ,您可以在 Google 上搜索 C++11 实现。

template<typename... Ts>
struct MoveTupleWrapper
{
    MoveTupleWrapper(std::tuple<Ts...>&& tuple) : m_tuple(tuple) {}
    MoveTupleWrapper(MoveTupleWrapper& other) : m_tuple(std::move(other.m_tuple)) {}
    MoveTupleWrapper& operator=(MoveTupleWrapper& other) { m_tuple = std::move(other.m_tuple); }
    std::tuple<Ts...> m_tuple;

    template<typename T, typename... Params>
    void apply(void (T::*member)(Params...), T& object)
    {
        applyHelper(member, object, std::index_sequence_for<Ts...>);
    }
    template<typename T, typename... Params, size_t... Is>
    void applyHelper(void (T::*member)(Params...), T& object, std::index_sequence<Is...>)
    {
        (object.*member)(std::move(std::get<Is>(m_tuple))...);
    }
};

template<typename... Ts>
auto MoveTuple(Ts&&... objects)
    -> MoveTupleWrapper<std::decay_t<Ts>...>
{
    return std::make_tuple(std::forward<Ts>(objects)...);
}

template<typename T, typename... Params>
struct WeakTaskPoster
{
    WeakTaskPoster(const boost::weak_ptr<IWorkQueue>& queue,
                   void (T::*member)(Params...),
                   const boost::weak_ptr<T>& weak) :
        m_queue(queue),
        m_member(member),
        m_weak(weak)
    {}
    boost::weak_ptr<IWorkQueue> m_queue;
    void (T::*m_member)(Params...);
    boost::weak_ptr<T> m_weak;

    template<typename... XParams>
    void operator()(XParams&&... params) const
    {
        if (auto qp = m_queue.lock())
        {
            auto weak = m_weak;
            auto member = m_member;
            auto tuple = MoveTuple(std::forward<XParams>(params)...);
            qp->Post([weak, member, tuple]() mutable
            {
                if (auto strong = weak.lock())
                {
                    tuple.apply(member, *strong);
                }
            });
        }
    }
};

template<typename T, typename... Params>
auto PostWeakTask(const boost::weak_ptr<IWorkQueue>& queue,
                  void (T::*member)(Params...),
                  const boost::weak_ptr<T>& weak)
    -> WeakTaskPoster<T, Params...>
{
    return { queue, member, weak };
}

请注意 WeakTaskPoster::operator()是一个模板,所以我们得到完美转发。执行此操作的 C++14 方法将是一个通用的 lambda。然后就是这个MoveTuple事物。这是 std::tuple 的包装器它简单地根据移动实现复制,因此我们可以解决 C++14 lambda 移动捕获的不足。它还完全实现了 std::apply 的子集。这是必要的。

这与 Yakk 的回答非常相似,除了参数元组被移动而不是复制到 lambda 的捕获集中。<​​>

关于C++11 lambda 和参数包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35760453/

有关C++11 lambda 和参数包的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  4. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  5. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  6. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  9. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  10. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

随机推荐