由值持有的成员的get函数应该具有什么类型

What type should a get function for a member held by value have?

本文关键字:类型 什么 get 成员 函数      更新时间:2023-10-16
class A { /* */ };
class B {
private:
A mA;
};

假设我们不想在这里按值返回B::mA——我们想精确地操作B内部的实例,调用它上的函数,等等。我们应该为B::getA()使用什么类型?我们的选择是:

  1. A*。可以在调用方删除,也可以存储在过去的对象生存期中。

  2. A&。如果调用方使用A a = b->getA()而不是A& a = b->getA(),则可能导致创建副本。

  3. CCD_ 8加上CCD_。抑制意外的副本创建,但现在A取决于B的实现细节。将ref存储在过去对象生存期的问题仍然存在。

  4. 弱指针。同样的存储问题加上很多混乱——弱指针不适合这样。

  5. 一些人工模板化的指针类型意味着只作为一条消息,而不是删除或存储它。工作但笨拙。

我错过了什么吗?

我建议返回A&-该方法的用户确实会滥用它,但C++中的几乎所有内容都是如此,并且返回对某个内容的引用是惯用的。

尽管如此,如果你想让客户端更难(但仍然不是不可能)滥用你的getter,可以考虑返回一个std::reference_wrapper<A>,这会让意外复制变得更困难(*):

std::reference_wrapper<A> a = some_b.get_a();
auto b = a; // doesn't copy `A` - it copies its address
A c = a; // copies, because `a` is implicitly convertible to `A&`

这种方法的缺点是std::reference_wrapper::get()必须用于访问A的成员(因为我们仍然没有可重载的operator.)


(*):制作意外副本是您最不关心的问题。即使在some_b死后,用户也可以保留对some_b.mA的引用,从而创建悬空引用!这种错误是Rust在编译时发现的,这要归功于它的借用检查器

如果有充分的理由使上述情况变得非常困难(但仍然不是不可能!),您可以使用更高阶函数而不是getter:

class A { /* */ };
class B {
private:
A mA;
public:
template <typename TF>
void mutate_a(TF&& f) 
{ 
// <`static_assert` that `f` takes `A&` here>
f(mA); 
}
};

用法:

B some_b;
some_b.mutate_a([](A& a)
{
// <do something with `a` here>
});

正如您所看到的,在lambda之外使用a比较困难,但语法开销只有在非常特殊的情况下才值得。我对退回A&和"信任用户"的建议仍然有效。