Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/152.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ lambda表达式移动捕获的计时_C++_C++14_Sequence_Undefined Behavior_Evaluation - Fatal编程技术网

C++ lambda表达式移动捕获的计时

C++ lambda表达式移动捕获的计时,c++,c++14,sequence,undefined-behavior,evaluation,C++,C++14,Sequence,Undefined Behavior,Evaluation,当我使用Boost.Asio时,创建对象,如ip::tcp::socket或deadline\u timer作为std::shared\u ptr,并将捕获的对象作为lambda表达式复制到完成处理程序 我很好奇如果我使用移动捕获而不是复制捕获会发生什么。我认为这很危险。在下面的示例中,我认为tim=std::move(tim)是在tim->async\u wait之前计算的。因此,tim不再具有有效指针。这是我的猜测。为了跟踪std::shared_ptr的行为,我创建了std::shared

当我使用Boost.Asio时,创建对象,如
ip::tcp::socket
deadline\u timer
作为
std::shared\u ptr
,并将捕获的对象作为lambda表达式复制到完成处理程序

我很好奇如果我使用移动捕获而不是复制捕获会发生什么。我认为这很危险。在下面的示例中,我认为
tim=std::move(tim)
是在
tim->async\u wait
之前计算的。因此,tim不再具有有效指针。这是我的猜测。为了跟踪
std::shared_ptr
的行为,我创建了
std::shared_ptr
wrapper
shared_ptr

#include <iostream>

#include <boost/asio.hpp>

namespace as = boost::asio;

template <typename... Args>
struct shared_ptr : std::shared_ptr<Args...> {
    using base = std::shared_ptr<Args...>;
    using base::base; // inheriting constructor

    shared_ptr(shared_ptr&& other) : base(std::move(other)) {
        std::cout << "move" << std::endl;
    }
    typename base::element_type* operator->() {
        std::cout << "->" << std::endl;
        return base::get();
    }
};

int main() {
    as::io_context ioc;

    ioc.post( 
        [&] {
            shared_ptr<as::deadline_timer> tim(new as::deadline_timer(ioc));
            tim->expires_from_now(boost::posix_time::seconds(1));
            tim->async_wait( 
                // I think that it is dangerous because tim has been moved before tim->async_wait()
                [&, tim = std::move(tim)] 
                    std::cout << ec.message() << std::endl;
                }
            );
        }
    );

    ioc.run();
}
输出B

->
move
->
Segmentation fault
似乎clang++和g++7.1.0或更高版本首先计算
tim->async\u wait()

g++6.3.0首先计算
tim=std::move(tim)


这是一种未定义的行为吗?或者在某个点定义了求值顺序?

C++17中的求值顺序定义良好,因此


但是,这是未指定的,因为。也就是说,它可以工作,也可以不工作,而且实现不需要告诉您它选择哪种方式,甚至不需要从一个调用到另一个调用保持一致。

我目前没有标准的措辞,但我很确定这是一种未定义的行为,因为您是
tim->async\u wait
call作用于已从中移动的对象。谢谢您的回答。这意味着如果编译器完全支持C++17,
tim=std::move(tim)
是更好的选择。由于共享计数未更改,因此它的性能(速度)高于复制实现。是吗?@TakatoshiKondo:在这种情况下,性能是不相关的,因为您无论如何都要执行异步分派操作。我怀疑原子的增量/减量是否会在性能方面产生差异。你的意思是你认为原子的增量/减量总体上会产生性能差异,但在这种情况下,它不是主要因素。我理解正确吗?如果它是一个成员函数,
tim->async_wait
在其参数之前排序并不重要-在
move
已经发生之后,它仍然会用pre-move
this
指针调用。@Eric:如果它在之前排序,这与它的工作方式无关。事实上,在开始工作之前,需要对其进行排序。问题是它是否在之后排序。计算参数后,
tim
不再具有有效指针。因此,您必须在计算参数之前获取指针,否则该操作将不起作用。
->
move
->
Segmentation fault