左值和右值getter,有可能删除它们吗

  • 本文关键字:删除 有可能 getter c++ c++20
  • 更新时间 :
  • 英文 :


假设我们有一个简单的类

struct MyClass {
const std::string &getString() const & {
return m_string;
}
std::string getString() && {
return std::move(m_string);
}
private:
std::string m_string;
};

正如我们所看到的,m_string是一个不可变的变量,我们不能修改它

这种结构还保留了这样一个事实,即如果我们将MyClass的一个实例移动到另一个实例,那么m_string属性也将被移动。

现在,我们将尝试重构先前的结构:

struct MyClass {
std::string m_string;
};

在这里,我们保留了这样一个事实,即我们可以访问或移动它,但我们失去了";不变性";。。。所以我试着这样写:

struct MyClass {
const std::string m_string;
};

这里我们得到了不变性,然而,当我们移动对象时,我们失去了潜在的优化。。。

那么,在不编写所有getter的情况下,是否可以有类似于第一段代码的行为?

编辑:std::string只是一个例子,但这个想法必须适用于所有类型的对象

那么,在不编写所有getter的情况下,是否可以有类似于第一段代码的行为?

我想不出任何。

话虽如此,为成员变量编写getter和setter的开销并不是一个很大的负担,我不会花太多时间思考它

然而,有些人认为成员变量的getter和setter并没有为类添加足够的保护,甚至不必担心它们。如果你认同这种思路,你就可以完全摆脱getter和setter。

我使用了";没有getter和setter";原则的数据容器足够的时间,我发现它在许多用例中是自然的。

您可以使用模板包装器类型来实现此行为。您似乎想要一种能很好地处理复制和移动构造和赋值的类型,但它只提供对包装对象的const访问。您只需要一个带有转发构造函数、隐式转换运算符和取消引用运算符的包装器(在隐式转换不起作用时强制转换(:

template<class T>
class immutable
{
public:
template<class ... A>
immutable(A&&... args) : member(std::forward<A>(args)...) {}
public:
operator const T &() const { return member; }
const T & operator*() const { return member; }
const T * operator->() const { return &member; }
private:
T member;
};

这将很好地与编译器生成的复制和移动构造和赋值配合使用。然而,该解决方案并非100%透明。如果上下文允许,包装器将隐式转换为对包装类型的引用:

#include <string>
struct foo
{
immutable<std::string> s;
};
void test(const std::string &) {}
int main()
{
foo f;
test(f.s); // Converts implicitly
}

但在隐式转换不起作用的情况下,它需要一个额外的解引用来强制转换:

int main()
{
foo f;
//  std::cout << f.s;   // Doesn't work
std::cout << *(f.s);    // Dereference instead
//  f.s.size();         // Doesn't work
f.s->size();            // Dereference instead
}

有人提议增加.运算符的重载,这将允许大多数情况按预期工作,而不需要取消引用。但我不确定该提案的现状

解决方案是使用std::shared_ptr<const std::string>。指向不可变对象的共享指针具有值语义。写时复制可以使用shared_ptr::unique()实现。参见Sean Parent演示47:46https://youtu.be/QGcVXgEVMJg.

如果您愿意将复制和移除ctor声明为=default并使用const_cast欺骗定义移动ctor就好了。

MyClass::Myclass()=default;
MyClass::Myclass(Myclass const&)=default;
MyClass::Myclass(Myclass && m)
:   m_string{std::move(const_cast<std::string&>(m.m_string))}{};

最新更新