持有大共享状态的访问者类:实现引用语义的最佳方式
Visitor class holding large shared state: best way to implement reference semantics?
这个问题大致基于Boost。图形库(BGL),它使用类似访问者的模式来定制递归(搜索)算法。BGL按值传递访问者对象(类似于STL函数对象),文档声明
由于visitor形参是按值传递的,如果visitor包含state,则在算法过程中对状态的任何更改都将对visitor对象的副本进行修改,而不是对传入的visitor对象进行修改。因此,您可能希望访问者通过指针或引用来保持此状态。
我的问题:实现有状态访问者类的引用语义的最佳方法是什么?从精确的指针类(原始的、唯一的、共享的、const的、非const的)抽象出来,什么是放置引用的最佳位置:在参数传递中还是在数据成员中?
选项1: visitor通过指针保存状态,并按值传递(如Boost.Graph)
class Visitor
{
public:
Visitor(): state_(new State()) {}
void start() { /* bla */ }
void finish() { /* mwa */ }
private:
State* state_;
}
template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor v)
{
v.start();
algorithm(next(n), v);
v.finish();
}
替代方案2: visitor按值保存数据,并按指针传递
class Visitor
{
public:
Visitor(): state_() {}
void start() { /* bla */ }
void finish() { /* mwa */ }
private:
State state_;
}
template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor* v)
{
v->start();
algorithm(next(n), v);
v->finish();
}
我目前的倾向:我发现选项1[保存指针/引用的对象按值传递]有点不舒服,因为访问者不满足值语义,所以我宁愿在参数列表[选项2]中明确引用语义。还有其他相关的考虑或选择吗?
还有第三种选择:
class Visitor
{
public:
Visitor(): state_() {}
void start() { /* bla */ }
void finish() { /* mwa */ }
private:
State state_;
};
template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor v)
{
v.start();
algorithm(next(n), v);
v.finish();
}
// Set the reference semantics here, use value everywhere else
algorithm(myNode, boost::ref(myVisitor)); // ... or std::ref in c++11
我认为这通常是标准所青睐的,而不是显式地将某物标记为指针或引用。毕竟,std::ref
和std::cref
已经被引入来解决这个问题。
另一方面,在《c++编码标准》一书中,Sutter和Alexandrescu认为函数函数应该总是易于快速复制的。他们建议在内部使用引用计数状态块(IIRC,这里没有书)。因此,虽然std::ref
或std::cref
可以解决您的问题,但它们更常用于"适应"非函子对象,例如,当通过std::bind
传递std::vector
时。
选项1,使用shared_ptr<T>
(或者更好:shared_ptr<T const>
)可能是您的最佳选择。在任何一种情况下,你只是"包装"你的指针语义后面的值语义的BGL代码,这是可以的,只要你得到所有的对象生存期正确。
我理解您对替代方案1的不适,但我认为这是"公共汽车已经离开"的情况;换句话说,c++标准库(以及Boost,而不仅仅是BGL)的方向倾向于使用"保留引用"模式。
考虑一下,例如,可以用lambda表达式实现的函数的广泛使用。据我所知,所有标准库(和boost)接口都按值传递函数函数参数,因此,如果函数函数保持状态,它必须通过引用来保持状态。因此,我们应该习惯看到[&](){}
而不是[=](){}
。并且,通过类比,我们应该习惯看到访问者持有指向其状态的引用(或指针,但我更喜欢引用)。
实际上有一个很好的理由通过值而不是通过引用传递函子(和访问者)。如果它们是通过引用传递的,那么它们要么由const&
传递,这将使状态修改变得不可能,要么由&
传递,这将使使用临时值变得不可能。唯一的另一种可能是传递一个显式指针,但这不能与lambdas或临时值一起使用(除非临时值被不必要地堆分配)。
试图使你的访问者无状态是没有意义的,当它实际上有一个状态。我看(2)没有任何问题。
- 如何在c++中使用引用实现类似python的行为
- 引用计数类的库实现
- unique_ptr实现接口时对已删除函数的引用
- 在函数重载中将右值引用实现为参数
- 对具有实现的函数的未定义引用
- 按值/引用传递定义的实现还是行为明智
- 函数描述与C++引用上的可能实现
- 从用户定义的头文件调用函数时出现未定义的引用错误,其实现位于.cpp文件中
- 引用计数智能指针的正确实现
- std::shared_ptr<std::string const> 可以作为引用计数的不可变字符串的有效实现吗?
- 在实现OpenSceneGraph时在Qt中获取引用问题
- 在我的C++链表实现中取消引用节点指针,给出意想不到的结果
- 使用const_cast const-ref getter 实现引用获取器的陷阱
- 如何为具有自引用指针的类实现复制构造函数/赋值运算符
- 尝试实现通过引用传递的向量以与二叉树一起使用,我错过了什么
- 如何在不使用指针的情况下实现引用中的链表
- 使用 C++ 实现引用计数
- 实现引用计数
- 是否可以在没有指针的情况下实现引用传递
- 持有大共享状态的访问者类:实现引用语义的最佳方式