浅谈`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的使用场景:

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

参考引用:

相关推荐
一只小小的芙厨6 小时前
AT_tkppc3_d 巨大チェスボード 题解
c++·题解
我在人间贩卖青春6 小时前
C++之继承与派生类的关系
c++·向上造型·向下造型
Trouvaille ~6 小时前
【Linux】应用层协议设计实战(二):Jsoncpp序列化与完整实现
linux·运维·服务器·网络·c++·json·应用层
EmbedLinX6 小时前
嵌入式之协议解析
linux·网络·c++·笔记·学习
wangjialelele7 小时前
Linux中的进程管理
java·linux·服务器·c语言·c++·个人开发
历程里程碑7 小时前
普通数组----轮转数组
java·数据结构·c++·算法·spring·leetcode·eclipse
李日灐7 小时前
C++进阶必备:红黑树从 0 到 1: 手撕底层,带你搞懂平衡二叉树的平衡逻辑与黑高检验
开发语言·数据结构·c++·后端·面试·红黑树·自平衡二叉搜索树
汉克老师7 小时前
GESP2025年6月认证C++二级( 第一部分选择题(1-8))
c++·循环结构·表达式·分支结构·gesp二级·gesp2级
rainbow68897 小时前
C++高性能框架Drogon:后端开发新标杆
c++
Q741_1477 小时前
C++ 优先级队列 大小堆 模拟 力扣 703. 数据流中的第 K 大元素 每日一题
c++·算法·leetcode·优先级队列·