指向矢量元素的持久指针,代替“ this”

时间:2018-07-27 13:26:10

标签: c++ pointers stdvector

我需要持久指向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

4 个答案:

答案 0 :(得分:1)

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

#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();
}

答案 1 :(得分:1)

您正在将其绑定到lambda的inizialization站点,因此,在任何情况下,即使确实移动或复制了对象,也无法使用。

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

#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();
}

可能不是最抢眼的解决方案,但它可以工作。

答案 2 :(得分:1)

您需要在move构造函数中重新定义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

答案 3 :(得分:1)

std::vector<A>的元素必须在不涉及移动构造函数A(A&&)的情况下进行设置。移动后,旧的this晃来晃去。我们称它为移动问题。

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

有多种解决此问题的方法:

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

  • 在move构造函数中更新lambda表达式(请参阅@Jack和@KillzoneKid的解决方案)

  • 不是在常规构造函数中初始化lambda表达式,而是在move构造函数中对其进行更新,而是插入一个单独的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应用程序进行了数天的试验之后,我得出的结论是,通过将相关逻辑移至父级来避免要好得多类(在此处显示的最小示例中:到main)。