为什么 boost::signals2::signal:<T>:connect 需要复制构造函数?
Why does boost::signals2::signal<T>::connect require copy constructors?
我正在研究一个表示正常数据值的对象,它利用函数式响应式编程,当依赖值发生变化时,它会改变它们的值。我的意思是,假设你有一个var3 = var1 + var2;当你改变var1的值时,var3的值也会自动更新。在c++中,这很困难,但是当更新函数在另一个工作线程中被调用时,你可以给它一个功能响应的外观。
这就是我的方法。我创建了一个名为Reactive的模板对象,它可以有任何类型,然后重载它的操作符,这样当两个反应加在一起时,不仅结果值等于它们的和,而且生成一个lambda,它将操作存储在std::函数中,该函数可以在以后调用它的更新函数时再次调用,以更新结果的值。
出现了几个问题。如果其中一个依赖值被破坏了怎么办?即使生成的react仍然拥有它认为有效的lambda,但lambda所使用的参数已经不存在了。为了说明这一点,我转向boost::signals2来建立一个信号和槽的系统,以通知其任何依赖项的破坏结果。当结果接收到信号时,它的影响函数将被清空,并且不会在更新时被调用。
对于能够执行+操作的响应,必须创建一个临时响应,它有自己的信号,然后必须重载=操作符以将临时中的数据移动到生成的响应中。然而,信号不能被复制。我通过在std::unique_ptr中包含destroy信号来避开这个问题,当操作符=接收到Reactive &&时使用std::move。避免危机。
现在我被困在这里了。我后来意识到,尽管移动结构很好,但仍然没有办法复制我的一个反应物结构,因为,让我们说var3 = var1 + var2;然后这样写:var4 = var3;然后,不知何故,var1和var2中的销毁信号需要一种方式通知var4它们已被销毁。我最后想到的是设置它,这样我就有一个名为Proxy的子对象,它是一个函子,包含boost::signals2::signal,然后每个对象都有一个包含在std::shared_ptr中的子对象。如果一个Reactive有对代理的引用,那么它就会把它的inform destroy方法连接到那个代理。然后被依赖者将这些信号附加到代理上。当代理被调用时,它也会调用它的所有连接。这也允许副本接收来自依赖项的销毁信号。
问题是,将代理连接到依赖信号需要代理具有复制构造函数,或者至少,这是msvc给我的错误。显然boost::signals2::signal::connect使用了它的复制构造函数,但它不能这样做,因为代理本身包含一个信号。我给你所有这些信息是因为我仍然不确定这是否是最好的解决方案。我选择了信号和插槽,因为我最熟悉它们,但如果你有更好的解决方案,请指出来。否则,请帮我避免这个错误。
顺便说一句,Slot只是使Unreact()函数成为一个函子的一种方式,对每个Reactive都是唯一的。
对象:
template<class T>
class Reactive
{
template<class H>
friend class Reactive;
class Slot : public boost::signals2::trackable
{
public:
Slot(std::function<void()> & func) :
m_Func(func)
{}
void operator()()
{m_Func();}
private:
std::function<void()> m_Func;
};
class Proxy : public boost::signals2::trackable
{
Proxy(const Proxy & s);
Proxy & operator=(const Proxy & s);
public:
Proxy(){}
void operator()()
{m_Informer();}
void attach(Slot & m_Unreacter)
{m_Informer.connect(m_Unreacter);}
private:
boost::signals2::signal<void()> m_Informer;
};
public:
~Reactive()
{
(*m_SendDestruct)();
}
Reactive() :
m_SendDestruct(new boost::signals2::signal<void()>),
m_Proxy(new Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(nullptr)
{
m_Proxy->attach(m_ReceiveDestruct);
}
template<class H>
Reactive(const H & data) :
m_SendDestruct(new boost::signals2::signal<void()>),
m_Proxy(new Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(nullptr),
m_Data(data)
{
m_Proxy->attach(m_ReceiveDestruct);
}
Reactive(const Reactive & reac) :
m_SendDestruct(new boost::signals2::signal<void()>),
m_Proxy(reac.m_Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(reac.m_Affecter),
m_Data(reac.m_Data)
{
m_Proxy->attach(m_ReceiveDestruct);
}
Reactive(Reactive && reac) :
m_SendDestruct(std::move(reac.m_SendDestruct)),
m_Proxy(reac.m_Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(reac.m_Affecter),
m_Data(reac.m_Data)
{
m_Proxy->attach(m_ReceiveDestruct);
}
Reactive & operator=(const T & data)
{
m_Data = data;
return *this;
}
Reactive & operator=(const Reactive & reac)
{
m_Proxy = reac.m_Proxy;
m_Proxy.attach(m_ReceiveDestruct);
m_Affecter = reac.m_Affecter;
m_Data = reac.m_Data;
}
Reactive & operator=(Reactive && reac)
{
m_SendDestruct = std::move(reac.m_SendDestruct);
m_Proxy = reac.m_Proxy;
m_Affecter(reac.m_Affecter);
m_Data(reac.m_Data);
}
template<class H>
Reactive & operator+(const H & rhs)
{
m_Data += rhs;
return *this;
}
template<class H>
auto operator+(Reactive<H> & rhs) -> Reactive<decltype(m_Data + rhs.m_Data)> &&
{
Reactive<decltype(m_Data + rhs.m_Data)> m_temp;
std::function<decltype(m_Data + rhs.m_Data)()> func;
if (!rhs.m_Affecter)
func = [&](){ return m_Data + rhs.m_Data;};
else
func = [&](){return m_Data + rhs.m_Affecter();};
m_SendDestruct->connect((*m_temp.m_Proxy));
rhs.m_SendDestruct->connect((*m_temp.m_Proxy));
return std::forward<Reactive<decltype(m_Data+rhs.m_Data)> &&>(m_temp);
}
template<class H>
Reactive && operator+(Reactive<H> && rhs)
{
Reactive && m_Temp
}
T & Get()
{
return m_Data;
}
void Update()
{
if(m_Affecter)
m_Data = m_Affecter();
}
void Unreact()
{
m_Affecter = nullptr;
(*m_SendDestruct)();
}
private:
std::unique_ptr<boost::signals2::signal<void()> > m_SendDestruct;
std::shared_ptr<Proxy> m_Proxy;
Slot m_ReceiveDestruct;
std::function<T()> m_Affecter;
T m_Data;
};
和简单测试
int main()
{
Reactive<int> vel(10);
Reactive<int> acc(5);
Reactive<int> time(5);
Reactive<int> result = vel + acc + time;
system("PAUSE");
return 0;
}
和以下是警告/错误:
1>main.cpp(86): warning C4355: 'this' : used in base member initializer list
1> main.cpp(83) : while compiling class template member function 'Reactive<T>::Reactive(Reactive<T> &&)'
1> with
1> [
1> T=int
1> ]
1> main.cpp(174) : see reference to class template instantiation 'Reactive<T>' being compiled
1> with
1> [
1> T=int
1> ]
1>main.cpp(66): warning C4355: 'this' : used in base member initializer list
1> main.cpp(174) : see reference to function template instantiation 'Reactive<T>::Reactive<int>(const H &)' being compiled
1> with
1> [
1> T=int,
1> H=int
1> ]
1>main.cpp(56): warning C4355: 'this' : used in base member initializer list
1> main.cpp(53) : while compiling class template member function 'Reactive<T>::Reactive(void)'
1> with
1> [
1> T=int
1> ]
1>c:program files (x86)boostboost_1_53_0boostsignals2detailslot_template.hpp(156): error C2248: 'Reactive<T>::Proxy::Proxy' : cannot access private member declared in class 'Reactive<T>::Proxy'
1> with
1> [
1> T=int
1> ]
1> main.cpp(32) : see declaration of 'Reactive<T>::Proxy::Proxy'
1> with
1> [
1> T=int
1> ]
1> main.cpp(30) : see declaration of 'Reactive<T>::Proxy'
1> with
1> [
1> T=int
1> ]
1> c:program files (x86)boostboost_1_53_0boostsignals2detailslot_template.hpp(81) : see reference to function template instantiation 'void boost::signals2::slot0<R,SlotFunction>::init_slot_function<F>(const F &)' being compiled
1> with
1> [
1> R=void,
1> SlotFunction=boost::function<void (void)>,
1> F=Reactive<int>::Proxy
1> ]
1> main.cpp(135) : see reference to function template instantiation 'boost::signals2::slot0<R,SlotFunction>::slot0<Reactive<T>::Proxy>(const F &)' being compiled
1> with
1> [
1> R=void,
1> SlotFunction=boost::function<void (void)>,
1> T=int,
1> F=Reactive<int>::Proxy
1> ]
1> main.cpp(178) : see reference to function template instantiation 'Reactive<T> &&Reactive<T>::operator +<int>(Reactive<T> &)' being compiled
1> with
1> [
1> T=int
1> ]
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:04.20
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
我认为你的设计有问题。
响应变量的逻辑生命周期不应该与命名它的c++变量的生命周期相关联。
使用pImpl
模式,使每个反应变量的生命周期只要存在命名它的c++变量,或者只要存在引用它的另一个活动的反应变量。(std::shared_ptr
,也许有一些东西来检测循环引用,这在反应图中是不好的)。
当你移动时,你移动的是pImpl
内部的状态,而不是pImpl
本身——pImpl
有自己的指向反应状态的指针(ppImpl
),这对于临时反应(如A+B
)非常有用。
当你依赖A
时,你实际上是在依赖A->pImpl
,并且你增加了它的引用计数。
如果设置了A = B
,则表示A->pImpl
依赖于B->pImpl
,而不是A->pImpl
的反应状态是B->pImpl
的反应状态的副本。这是一个细微的差别。
这仍然需要一些努力来向前传播更改。我将返回weak_ptr
引用,指向那些依赖于你的人,并通过树向后传播SetIsDirty
。任何脏的值在读取时都会重新计算,否则会使用缓存的值。
如果您希望A = B
的效果使A->pImpl
成为B->pImpl
的副本,请使用不同的语法(如A = *B
,以窃取c++主义)。这是唯一需要复制pImpl
状态的情况!
注意A = std::move(B)
需要移动一个pImpl
状态,因此为了优化目的,使用ppImpl
来存储状态。
您应该发现实际上不需要额外的线程或插槽/套接字。除非您实现单一的operator*
,否则不需要复制给定pImpl
的状态。
请注意,我关于循环依赖的随口评论很重要。在上述设计下,具有循环依赖关系的响应图将会泄漏,更重要的是,在逻辑上不可能计算任何值。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 复制构造函数、赋值运算符C++
- std::ofstream 作为类成员删除复制构造函数?
- 复制构造函数C++无法正确复制指针
- 关于复制构造函数的一个棘手问题
- 为什么调用复制构造函数而不是移动构造函数?
- 填充上编译器生成的复制构造函数之间的不一致
- C++ 对象指针数组的复制构造函数
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 防止在复制构造函数中隐式调用基构造函数
- 为用户定义的类正确调用复制构造函数/赋值运算符
- 具有已删除移动和复制构造函数的类的就地构造
- 复制构造函数隐式转换问题
- 复制构造函数中的递归调用