浅谈`std::experimental::propagate_const`

The matters you reflect upon have been previously deliberated by others.

常写C++的都知道,const用来声明变量或对象的不变性,但当涉及到指针或类指针对象的时候,这种不变性的约束似乎被打破了。

cpp 复制代码
// ptr 本身不能被修改
const std::unique_ptr<int> ptr = std::make_unique<int>(42);
// ptr 指向的对象(即 int 类型的值)仍然可以被修改,这是合法的
*ptr = 100;

更进一步的一个例子(godbolt

cpp 复制代码
struct A
{
  void bar() const 
  { 
    std::cout << "bar (const)" << std::endl; 
  }
  
  void bar() 
  { 
    std::cout << "bar (non-const)" << std::endl; 
  }
};

struct B
{
  B() : m_ptrA(std::make_unique<A>()) {} 
  
  void foo() const 
  { 
    std::cout << "foo (const)" << std::endl;
    m_ptrA->bar(); 
  }           
  
  void foo() 
  { 
    std::cout << "foo (non-const)" << std::endl;
    m_ptrA->bar(); 
  }

  std::unique_ptr<A> m_ptrA;
};

int main()
{    
  B b;
  b.foo();
  
  const B const_b;
  const_b.foo();
}

得到的输出是:

arduino 复制代码
foo (non-const)
bar (non-const)
foo (const)
bar (non-const)

上述行为可以通过使用 const_cast 重写 void B::foo() const 来修正,以显式调用 Aconst 成员函数。但这样的改动并不优雅。

有其他办法么?std::experimental::propagate_const

它来自于2015年的一份提案:A Proposal to Add a Const-Propagating Wrapper to the Standard Library

简言之:std::experimental::propagate_const 是一个包装器类,用于确保 const 属性不仅应用于指针或类指针对象本身,还应用于其所指向的对象。从而达到在 const 对象中传播 const 属性的功能。

还是用示例来描述:

示例一godbolt

cpp 复制代码
#include <experimental/propagate_const>
#include <iostream>
#include <memory>

int main() {
    std::experimental::propagate_const<std::unique_ptr<int>> ptr = std::make_unique<int>(42);

    // 通过 const 访问路径访问时,ptr 被视为指向 const 的指针
    const auto& constPtr = ptr;
    std::cout << *constPtr << std::endl;  // 输出:42
    * constPtr = 50;        // 不合法,会有编译错误

    // 修改 ptr 指向的对象
    *ptr = 100;
    std::cout << *ptr << std::endl;  // 输出:100

    return 0;
}

会得到一个编译错误

shell 复制代码
# 编译输出
<source>: In function 'int main()':
<source>:11:16: error: assignment of read-only location '(& constPtr)->std::experimental::fundamentals_v2::propagate_const<std::unique_ptr<int> >::operator*()'
   11 |     * constPtr = 50;
      |     ~~~~~~~~~~~^~~~
Compiler returned: 1

示例二godbolt

cpp 复制代码
#include <experimental/propagate_const>
#include <iostream>
#include <memory>
 
struct X
{
    void g() const { std::cout << "X::g (const)\n"; }
    void g() { std::cout << "X::g (non-const)\n"; }
};
 
struct Y
{
    Y() : m_propConstX(std::make_unique<X>()), m_autoPtrX(std::make_unique<X>()) {}
 
    void f() const
    {
        std::cout << "Y::f (const)\n";
        m_propConstX->g();
        m_autoPtrX->g();
    }
 
    void f()
    {
        std::cout << "Y::f (non-const)\n";
        m_propConstX->g();
        m_autoPtrX->g();
    }
 
    std::experimental::propagate_const<std::unique_ptr<X>> m_propConstX;
    std::unique_ptr<X> m_autoPtrX;
};
 
int main()
{
    Y y;
    y.f();
 
    const Y cy; // Y 的 const 属性,自然的传递到了其内部变量 m_propConstX 上
    cy.f();
}

得到输出

arduino 复制代码
Y::f (non-const)
X::g (non-const)
X::g (non-const)
Y::f (const)
X::g (const)
X::g (non-const)

std::experimental::propagate_const的一些特性:

  • 如果 propagate_const 包装的对象是 const,则其指向的对象也会被视为 const
  • 如果 propagate_const 包装的对象是非 const,则其指向的对象也是非 const
  • propagate_const 支持包装智能指针(如 std::unique_ptrstd::shared_ptr),也支持包装普通指针
  • 对指针指向的访问是透明的:propagate_const 重载了 operator*operator->,使得访问包装的对象与直接访问指针一样方便

std::experimental::propagate_const的注意事项:

  • propagate_const 目前是 C++ 标准库的一个扩展,定义在 <experimental/propagate_const> 中),尚未正式纳入 C++ 标准
  • 需要确认编译器的支持情况,通常在C++17或更高版本的编译器上可用
  • propagate_const 是一个轻量级包装器,通常不会引入额外的性能开销

std::experimental::propagate_const的使用场景:

  • 确保对象及其成员(特别是成员中包含指针指向的间接成员)都是不可变时

参考引用:

相关推荐
技术求索者2 小时前
c++学习
开发语言·c++·学习
左直拳5 小时前
c++中“&”符号代表引用还是取内存地址?
开发语言·c++·指针·引用·右值·取内存地址
十年编程老舅6 小时前
二本计算机,毕业=失业?
c++·程序员·编程·秋招·c++项目·春招·qt项目
虾球xz6 小时前
游戏引擎学习第260天:在性能分析器中实现钻取功能
网络·c++·学习·游戏引擎
Yusei_05237 小时前
C++复习类与对象基础
c++
拾忆-eleven8 小时前
C++算法(19):整数类型极值,从INT_MIN原理到跨平台开发实战
数据结构·c++·算法
Hxyle8 小时前
c++设计模式
开发语言·c++·设计模式
神仙别闹9 小时前
基于QT(C++)实现(图形界面)校园导览系统
数据库·c++·qt
明月看潮生9 小时前
青少年编程与数学 02-018 C++数据结构与算法 25课题、图像处理算法
c++·图像处理·算法·青少年编程·编程与数学
我是一只鱼022310 小时前
LeetCode算法题 (反转链表)Day17!!!C/C++
数据结构·c++·算法·leetcode·链表