【问题标题】:Transform a tuple of rvalues into a tuple of lvalues将右值元组转换为左值元组
【发布时间】:2020-04-15 18:40:06
【问题描述】:

如果我把整个概念弄错了,我很抱歉,但我正在尝试将一个元组作为实际对象的容器,只有在销毁这些对象时,这些对象才会超出范围。

我目前有这个:

class MiniThread {
public:
    ~MiniThread() {
        if (m_thread) {
            if (m_thread->joinable())
                m_thread->join();

            delete m_thread;
        }
    }

    void join()
    {
        if (m_thread == nullptr)
            return;

        m_thread->join();
        m_thread = nullptr;
    }

    template<typename F, typename... Args>
    void run(F func, Args... args)
    {
        if (m_thread != nullptr)
            join();

        auto tuple = std::forward_as_tuple(args...);

        m_thread = new std::thread([=]() {
            __try
            {
                std::apply(func, tuple);
            }
            __except (CrashDump::GenerateDump(GetExceptionInformation()))
            {
                // TODO: log.
                exit(1);
            }
        });

        m_started = true;
    }

    bool started() const { return m_started; }

private:
    std::thread *m_thread = nullptr;
    bool m_started = false;
};


std::string getString()
{
    return std::string("sono");
}

int main()
{
    auto test = [&](std::string seila, const std::string& po, std::promise<int>* p)
    {
        std::cout << seila.c_str() << std::endl;
        std::cout << po.c_str() << std::endl;
        p->set_value(10);
    };

    std::promise<int> p;
    std::future<int> f;

    MiniThread thread;
    std::string hello = "hello";
    std::string seilapo = "seilapo";

    f = p.get_future();
    thread.run(test, getString(), "how are you", &p);

    thread.join();
    int ftest = f.get();
    std::cout << ftest << std::endl;
}

当线程运行时,args 不再可靠。他们已经被摧毁了。所以我想知道是否有办法在线程调用中复制它们按值。我已经尝试将可变参数移动到元组中,但是元组总是使用rvalues 呈现并且同样失败。

【问题讨论】:

  • 您在此处的代码已经复制了参数,并且在此示例中没有右值。此外,它的格式不正确(应该是args...)。所以请提供minimal reproducible example
  • 对不起。我修好了它。而且我不确定这里发生了什么,但是 __try / __exception 不允许这样做,因为它说参数不能从这个 SEH 中展开。
  • 我用我的实际实现更新了代码。此代码运行,但 std::apply(func, tuple) 的参数有意外值。
  • 更新了完全可复制的示例。

标签: c++ tuples c++17 variadic-templates


【解决方案1】:

这个:

auto tuple = std::forward_as_tuple(args...);

args... 中创建一个引用元组这就是forward_as_tuple 的工作。然后,您将按值捕获该引用元组:

m_thread = new std::thread([=]{ /* ... */ });

因此,一旦您的论点超出范围,您就只能保留对它们的引用……这将悬而未决。

但实际上你根本不需要有一个元组。只需复制参数本身:

m_thread = std::thread([=]() {
    func(args...); // func and args, no tuple here
});

同样不要写new thread - thread 已经是句柄类型,只需创建一个。


上述复制参数。如果您想移动它们,那么在 C++17 中,是的,您需要有一个 tuple 并使用 std::apply。但不是forward_as_tuple...只是make_tuple

m_thread = std::thread([func, args=std::make_tuple(std::move(args)...)]() mutable {
    std::apply(func, std::move(args));
});

在 C++20 中,您将不再需要 tuple,并且可以编写一个包扩展:

m_thread = std::thread([func, ...args=std::move(args)]() mutable {
    func(std::move(args)...);
});

【讨论】:

  • 你不应该使用std::forward吗?
  • @MarekR 没有任何转发引用,一切都是价值。
  • 你是对的。另一个问题:如果您在通过复制捕获时使用std::move,lambda 不应该是可变的吗?
  • @MarekR 是的,应该是。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
  • 2017-06-20
  • 1970-01-01
  • 2019-10-12
相关资源
最近更新 更多