【问题标题】:C++ Force const-ness of lvalue in initializer expressionC++ 在初始化表达式中强制左值的 const-ness
【发布时间】:2015-12-07 23:38:19
【问题描述】:

我希望编译器强制执行左值(非引用)的 const-ness,但不知道这在 C++ 中是否可行。一个例子:

int foo() { return 5; }

int main() {
  // Is there anything I can add to the declaration of foo()
  // that would make the following cause a compile-error?
  int a = foo();

  // Whereas this compiles fine.
  const int a = foo();
}

【问题讨论】:

  • 什么是“非参考左值”? “左值”是一种表达式,表达式永远不是引用。
  • 您的代码中仍然没有“左值”。您正在使用初始化表达式定义变量。
  • 既然您退回了副本,出于好奇,您为什么需要这个?
  • @ʎǝɹɟɟɟǝſ:这是一个左值引用,但不是“引用左值”。后者不存在。
  • 好的,那么它是一个左值引用。既然我们已经整理了这些技术细节,我们是否可以同意它们对于所提出的问题并不重要?

标签: c++ constants pass-by-value lvalue


【解决方案1】:

这对于int 之类的东西实际上是不可能的,因为您需要授予读取 int 的权限,如果他们可以读取 int,那么他们可以将其复制到非常量 int 中。

但从您的 cmets 看来,您实际上拥有的不是 int,而是更复杂的用户定义类型,可能是某种容器。您可以轻松创建不可变容器。该容器可以是包装器,也可以是现有容器的替代实现。然后,调用者使用 const 或非 const 变量并不重要,它仍然是不可变的。

class MyClass {
  std::vector<int> data;
public:
  MyClass(size_t size) : data(size) {}
  int& operator[](size_t index) { return data[index]; }
  int operator[](size_t index) const { return data[index]; }
  size_t size() const { return data.size(); }
};

class MyClassImmutable {
  MyClass mc;
public:
  MyClassImmutable(MyClass&& mc) : mc(std::move(mc)){}
  int operator[](size_t index) const { return mc[index]; }
  size_t size() const { return mc.size(); }
  const MyClass& get() const { return mc; }
};

MyClassImmutable foo() {
  MyClass mc(100);
  mc[10] = 3;
  return mc;
}

void func(const MyClass& mc);

int main() {
    MyClassImmutable mci = foo();
    std::cout << mci[10] << "\n";  // Can read individual values
    //mci[10] = 4; // Error immutable
    func(mc.get());  // call function taking a const MyClass&
}

Live demo.

当然,没有什么可以阻止调用者从不可变容器中复制每个值并将它们插入到可变容器中。

编辑:另一种方法可能是返回一个指向常量的智能指针。唯一的缺点是您必须为动态内存分配付费:

std::unique_ptr<const MyClass> foo() {
  auto mc = std::make_unique<MyClass>(100);
  (*mc)[10] = 3;
  return mc;
}

void func(const MyClass& mc);

int main() {
    auto mc = foo();
    std::cout << (*mc)[10] << "\n";  // Can read individual values
    //(*mc)[10] = 4; // Error const
    func(*mc);  // can pass to a function taking a const MyClass&
}

【讨论】:

  • 谢谢。这很酷,但不是我需要的。你说得对,ints 我并不真正需要它,而是容器类型。但问题是,稍后在代码中我需要将容器传递给各种现有函数,这些函数(在您的示例中)将const MyClass&amp; 作为参数,而不是MyClassImmutable。也许,我们可以在MyClassImmutable 中使用继承而不是组合吗?
  • @RafaelSpring MyClassImmutable 公开const MyClass&amp; 是合理的(添加)。您可以使用继承,例如您可以有一个只读接口 IMyClass 并返回一个指向只读接口的智能指针,但是您不能将它传递给采用 const MyClass&amp; 的函数(您可以更新所有这些功能都使用接口)。我添加了一个可能可行的替代方案。
  • 啊,不错。我自己也可以想到这一点,所以感谢您明确指出。
  • @RafaelSpring 您可能会说暴露const MyClass&amp; 有点失败,因为您可以直接复制到可变副本:MyClass m = foo().get() 这正是您试图避免的。
  • 是的,但不是我想要避免的。由于您返回const MyClass&amp;,因此底层缓冲区仍然是不可变的。您始终可以制作对象的副本,就像您可以制作const int 的副本一样。当它是 const 时,您无法更改其中的内容。
【解决方案2】:

这是不可能的。 foo() 无法知道赋值左侧的类型,因为当赋值本身发生时,foo() 已经被评估。您可能希望的最好结果是更改返回值,尝试在初始化时导致基于类型的错误:

#include <type_traits>

struct my_int {
  const int m;
  template<typename T, typename std::enable_if<std::is_const<T>::value, T>::type* = nullptr>
  constexpr operator T() const {return m;}
};

constexpr my_int foo() { return {5};}

int main() {
  const int a = foo();
  int b = foo();
}

Live example

但这也不起作用,因为模板中的类型名永远不会被 const 限定类型替换(在这种特定情况下,main() 中的两行都将是 int)。

【讨论】:

    【解决方案3】:

    以下是可能的

    const int x = 4;
    int y = x;
    

    C++ 语言不会提供这样的机制。

    仍然通过宏机制制作 int const。

    #define int_const_foo(var) const int var = ___foo()
    
    int_const_foo(a);
    

    缺点:foo不能隐藏,语法不再是C风格。

    【讨论】:

      猜你喜欢
      • 2021-04-13
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 2010-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多