由值持有的成员的get函数应该具有什么类型
What type should a get function for a member held by value have?
class A { /* */ };
class B {
private:
A mA;
};
假设我们不想在这里按值返回B::mA
——我们想精确地操作B
内部的实例,调用它上的函数,等等。我们应该为B::getA()
使用什么类型?我们的选择是:
A*
。可以在调用方删除,也可以存储在过去的对象生存期中。A&
。如果调用方使用A a = b->getA()
而不是A& a = b->getA()
,则可能导致创建副本。CCD_ 8加上CCD_。抑制意外的副本创建,但现在
A
取决于B
的实现细节。将ref存储在过去对象生存期的问题仍然存在。弱指针。同样的存储问题加上很多混乱——弱指针不适合这样。
一些人工模板化的指针类型意味着只作为一条消息,而不是删除或存储它。工作但笨拙。
我错过了什么吗?
我建议返回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&
和"信任用户"的建议仍然有效。
- 我应该使用什么来代替void作为变体中的替代类型之一
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 数据类型"struct seq<0, 1, 2>{}"含义是什么?
- 在自定义 std::vector-like 容器中处理指针和非指针模板类型的最佳方法是什么?
- 您应该在什么时候创建自己的异常类型
- C++ 未初始化的本地(非全局)int 数组中的元素类型到底是什么?
- 未命名的非类型模板参数有什么意义?
- 静态自动 constexpr t = { "red" , "black" , "green" } 是什么类型;派生到?
- 模板<>模板<类型名 T> 语法有什么用?
- 指针类型类成员的动态强制转换的恒定性是什么?
- 当 c++ 需要一种数据类型并获取另一种数据类型时会发生什么?
- 这是什么类型的C++语法,我应该采取什么步骤来理解这一点
- 返回类型在 C++ OOP 中是什么意思
- 如何声明一个标准::提升直方图的向量?提升直方图的类型是什么?
- 类名后跟括号的类型是什么
- 删除对象(具有不同类型)的引用时会发生什么情况?
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 当你只使用 return 时,函数返回什么类型;在 c++ 中