Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.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++ 复制/移动gcc中的对象会断开其成员lambda_C++_Gcc_Lambda_Polymorphism_Visitor - Fatal编程技术网

C++ 复制/移动gcc中的对象会断开其成员lambda

C++ 复制/移动gcc中的对象会断开其成员lambda,c++,gcc,lambda,polymorphism,visitor,C++,Gcc,Lambda,Polymorphism,Visitor,比如说,有一个简单的多态关系: class base { public: void update() const { updater(); } void accept(visitor const &visitor) { visitor_acceptor(visitor); } protected: base(std::function<void()> const &updater, std::func

比如说,有一个简单的多态关系:

class base {
public:
    void update() const { updater(); }
    void accept(visitor const &visitor) {
        visitor_acceptor(visitor);
    }
protected:
    base(std::function<void()> const &updater, 
         std::function<void(visitor const &)> const &visitor_acceptor) 
        : updater(updater), visitor_acceptor(visitor_acceptor) {}
protected:
    std::function<void()> updater;
    std::function<void(visitor const &)> visitor_acceptor;
};

class derived_int : public base {
public:
    derived_int() : base(
            [this]() { ++value; }, 
            [this](visitor const &visitor) { visitor(value); }
        ), value(0) {}
protected:
    int value;
};

class derived_string : public base {
public:
    derived_string() : base(
            [this]() { value += '1'; }, 
            [this](visitor const &visitor) { visitor(value); }
        ), value("") {}
protected:
    std::string value;
};

似乎存在某种对象生命周期问题,因为显式创建的变量不会导致任何问题,但我无法准确地指出它。

一旦捕获
函数对象成员就不适合复制或移动。捕获的
this
是一个副本,仍然指向生成lambda的原始实例。您复制的
this
可能不再是有效指针,使用它是未定义的行为。它有时可能会起作用,可能会使其他人崩溃,可能会做任何可以想象的事情。不同的编译器可以生成不同的程序集。就语言而言,对于一个实现的UB表达式与另一个实现的UB表达式如何以及为什么不同,没有任何逻辑或理由。如果您试图自己处理移动和复制,您仍然需要创建具有不同捕获值的全新lambda。您将无法提取原始lambda函数体来执行此操作。您可以尝试将
等效参数添加到函数对象中,而不是捕获
,但我不确定这是否与您的设计兼容。一个可能的快速解决方法是使您的类型不可复制且不可移动,并始终使用
std::unique\u ptr
对其进行管理。如果您从未复制或移动您的类型,则lambda捕获的指针永远不会失效。您仍然可以添加
emplace
以使
s.emplace(/*..*/)
一旦捕获
函数对象成员就不适合复制或移动。捕获的
this
是一个副本,仍然指向生成lambda的原始实例。您复制的
this
可能不再是有效指针,使用它是未定义的行为。它有时可能会起作用,可能会使其他人崩溃,可能会做任何可以想象的事情。不同的编译器可以生成不同的程序集。就语言而言,对于一个实现的UB表达式与另一个实现的UB表达式如何以及为什么不同,没有任何逻辑或理由。如果您试图自己处理移动和复制,您仍然需要创建具有不同捕获值的全新lambda。您将无法提取原始lambda函数体来执行此操作。您可以尝试将
等效参数添加到函数对象中,而不是捕获
,但我不确定这是否与您的设计兼容。一个可能的快速解决方法是使您的类型不可复制且不可移动,并始终使用
std::unique\u ptr
对其进行管理。如果您从未复制或移动您的类型,则lambda捕获的指针永远不会失效。您仍然可以添加
emplace
以使
s.emplace(/*..*/)

class storage {
public:
    void add(base const &input) { pointers.emplace_back(std::make_unique<base>(input)); }
    void add(base &&input) { pointers.emplace_back(std::make_unique<base>(std::move(input))); }

    void update() {
        for (auto &ptr : pointers)
            ptr->update();
    }
    void print() {
        visitor printer;
        for (auto &ptr : pointers)
            ptr->accept(printer);
    }
private:
    std::vector<std::unique_ptr<base>> pointers;
};
int main() {
    storage s;

    derived_int temp1;
    derived_string temp2;
    s.add(temp1);
    s.add(temp2);
    s.add(derived_int{});
    s.add(derived_string{});

    for (size_t i = 0; i < 5; i++)
        s.update();

    s.print();
}
derived_int: 5
derived_string: 11111
derived_int: -71963
derived_string: 1