考虑下面的简短程序。我使用std::function和lambda将成员函数对象(initialise)设置为同一类(initialiser)中的成员函数(A)。然后在4个地方调用initialise():1.在A中的main构造函数中; 2.在main中的直接构造函数中; 3.在A构造函数中的B构造函数中;以及4.直接从B的构造函数中调用。

    #include <iostream>
    #include <functional>
    #include <vector>

    class A {
    public:
        A(std::vector<int> const& sizes) : m_sizes(sizes) {
            set_initialiser();
            initialise();
        }
        A() = default;
        std::function<void()> initialise;
        void set_initialiser() { initialise = [this]() {return initialiser(); }; };
        // void set_initialiser() { initialise = std::bind(&A::initialiser, this); }; // the same occurs if I use bind instead of a lambda

    private:
        std::vector<int> m_sizes;
        void initialiser() {
            std::cout << "m_sizes size = " << m_sizes.size() << ", with contents:";
            for (auto & s : m_sizes)
                std::cout << " " << s;
            std::cout << std::endl;
        };
    };

    class B {
    public:
        B(std::vector<int> const& v) {
            a = A(v);
            a.initialise(); // here a.m_sizes and a.initialise.functor.this.m_sizes differ
        };
    private:
        A a;
    };

    int main(int argc, char* argv[])
    {
        auto a = A({ 4,3,2,1 });
        a.initialise();
        auto b = B({ 4,3,2,1 });
        return 0;
    }

编译并运行此代码会带来以下意外的行为(至少对我而言)。
m_sizes size = 4, with contents: 4 3 2 1
m_sizes size = 4, with contents: 4 3 2 1
m_sizes size = 4, with contents: 4 3 2 1
m_sizes size = 0, with contents:

任何人都可以阐明为什么最后一次调用initialise()包含未初始化的m_sizes吗?我怀疑这可能与在lambda中使用的this的不同实例有关,但是我不明白为什么从main调用它和从另一个类调用它应该有什么区别。

最佳答案

a = A(v);

该行创建一个临时对象A(v)(我将其称为t),并将其数据复制到a中。 t.initialise包含一个lambda,该lambda已按值捕获了this(即&t)。然后,您将此lambda复制到a.initialise中;但是请注意,它仍然引用&t

然后,完整表达式结束,t被销毁(因为它是临时的),并且a.initialise中捕获的指针现在悬空了。因此,对a.initialise()的下一次调用取消了悬空指针的引用,从而为您提供了不确定的行为。

请注意,您的main也发生了完全相同的问题(相同的未定义行为),但是其影响有所不同。接下来是对原因的推测,但请记住,未定义的行为是未定义的,任何事情都可能发生。

我想在main内部会发生复制省略,并且直接在A({ 4,3,2,1 })的空间中构造临时a,使其thisa相同,这使得对initialise的调用仍然有效。

B的构造函数中,不可能进行复制省略(因为您正在执行赋值操作而不是初始化),因此临时确实被销毁了,留下了正确销毁的 vector ,在您的情况下,该 vector 仍然与空 vector 相同(可能是因为它已从临时目录移到a)。

再说一遍:这仅仅是猜测,而代码根本就是错误的。

10-06 02:22