指向矢量元素的持久指针代替 '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; };
};
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
)。
我需要指向 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; };
};
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
)。