指向矢量元素的持久指针代替 'this'

persistent pointer to vector element in place of 'this'

我需要指向 std::vector 元素的持久指针。直接 this 不行,如下所示:

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

class A {
public:
    A() = delete;
    A(int i)
        : my_i(i)
        , whoAmI([this]()->void{ std::cout<<"I am class A with i="<<my_i<<std::endl; })
        {}
    A(const A&) = delete;
    A(A&&) = default;
    int my_i;
    std::function<void()> whoAmI;
};

int main()
{
    std::vector<A> vec;
    std::cout << "Initialization:" << std::endl;
    for (int i=0; i<3; ++i) {
        vec.push_back(A(i));
        vec.back().whoAmI();
    }
    std::cout << "Retrieval:" << std::endl;
    for (int i=0; i<3; ++i)
        vec.at(i).whoAmI();
}

产生

Initialization:
I am class A with i=0
I am class A with i=1
I am class A with i=2
Retrieval:
I am class A with i=2
I am class A with i=2
I am class A with i=2

而我需要

Retrieval:
I am class A with i=0
I am class A with i=1
I am class A with i=2

当然,在这个例子中,可以很容易地变通。在现实生活中,持久指针应传递给 Qt::connect.

也许尝试像这样按值捕获索引:

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


class A {
public:
    A() = delete;
    A(int i, std::vector<A> &vec)
        : whoAmI([i, &vec]()->void{
             std::cout<<"I am class A with i="<< i << ". Calling whoAmI(): "  << std::endl;
             vec[i];
        })
        {}
    A(const A&) = delete;
    A(A&&) = default;
    std::function<void()> whoAmI;


};

int main()
{
    std::vector<A> vec;
    std::cout << "Initialization:" << std::endl;
    for (int i=0; i<3; ++i) {
        vec.push_back(A(i, vec));
        vec.back().whoAmI();
    }
    std::cout << "Retrieval:" << std::endl;
    for (int i=0; i<3; ++i)
        vec.at(i).whoAmI();
}

您在 lambda 的初始化站点绑定它,因此在对象确实被移动或复制的任何情况下都不会起作用。

一个可能的解决方案是添加一个间接级别:

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

class A;
class fuction_wrapper
{
public:
  A* ref;
  std::function<void(A*)> lambda;
  void operator()() const { lambda(ref); }
};

class A {
public:
  A() = delete;
  A(int i)
  : my_i(i)
  , whoAmI({this, [] (A* a) { std::cout<<"I am class A with i="<< a->my_i << std::endl; }})
  {}
  A(const A&) = delete;
  A(A&& a)
  {
    my_i = std::move(a.my_i);
    whoAmI = { this, std::move(a.whoAmI.lambda) };
  }

  int my_i;
  fuction_wrapper whoAmI;
};

int main()
{
  std::vector<A> vec;
  std::cout << "Initialization:" << std::endl;
  for (int i=0; i<3; ++i) {
    vec.push_back(A(i));
    vec.back().whoAmI();
  }
  std::cout << "Retrieval:" << std::endl;
  for (int i=0; i<3; ++i)
    vec.at(i).whoAmI();
}

可能不是最吸引眼球的解决方案,但它确实有效。

您需要在移动构造函数中重新定义您的 lambda,以便它在移动后获得更新的 this,因为您推入的临时 A(i) 已不存在。

A(A&& a) 
    { 
        this->my_i = a.my_i; 
        this->whoAmI = [this]()->void { std::cout << "I am class A with i=" << this->my_i << std::endl; }; 
    };

https://ideone.com/s5ZyS5

Initialization:
I am class A with i=0
I am class A with i=1
I am class A with i=2
Retrieval:
I am class A with i=0
I am class A with i=1
I am class A with i=2

如果不涉及移动构造函数 A(A&&),则无法设置 std::vector<A> 的元素。一招之后,老this悬空。暂且称之为移动-这个问题吧。

此外,向量元素地址可能会随着向量的增长而变化。这个问题可以通过保留足够的向量大小,或者通过将向量元素封装在唯一指针中来解决。但是,none这些措施解决了移动这个问题,因此是更根本的。

move-this问题有几种解决方法:

  • 而不是使用 this,捕获拥有向量和向量索引(参见@bartop 的解决方案)

  • 更新移动构造函数中的 lambda 表达式(参见@Jack 和@KillzoneKid 的解决方案)

  • 不是在常规构造函数中初始化 lambda 表达式,而是在移动构造函数中更新它,而是插入一个单独的 init 函数:

class A {
    ...
    A(int i) : my_i(i) {}
 /* A(A&&) = default; */
    void init();
    ...
};

void A::init() {
    whoAmI = [this]()->void{ std::cout<<"I am class A with i="<<my_i<<std::endl; };
}

int main()
{
    std::vector<A> vec;
    std::cout << "Initialization:" << std::endl;
    for (int i=0; i<3; ++i) {
        vec.push_back(A(i));
        vec.back().init();
        vec.back().whoAmI();
    }
    ...
}

然而,在对我的 QObject::connect 应用程序进行了多天的实验之后,我得出的结论是,通过将 避免 整个问题与父 class 有关的逻辑(在此处显示的最小示例中:main)。