为何C++ const引用会折叠为非const引用?如何限制其可修改性?
A<int&>(x).get() = 43;能编译通过?如何让get()返回真正的const引用? 首先直接回答第一个问题:是的,这个行为完全是由C++的引用折叠规则导致的。
让我们拆解代码里的细节:
当你实例化A<int&>时,模板参数T被推导为int&。此时get()函数的返回类型const T&展开后是const (int&) &——这里要注意,C里引用本身不能被const修饰,所以const int&里的const是作用于引用指向的对象,而非引用本身。但当我们把T设为int&后,const T其实是试图给引用加const,这本身是无效的,再加上外层的&,就触发了引用折叠规则:
在C中,引用的引用会按照以下规则折叠:
T& &→T&T& &&→T&T&& &→T&T&& &&→T&&
所以const int& &最终会折叠成int&,这就意味着get()实际返回的是一个非const的左值引用,自然允许你对其返回值赋值,也就出现了代码里A<int&>(x).get() = 43;能编译通过的情况。
接下来解决第二个问题:如何让get()返回真正的const引用,禁止修改指向的值?
核心思路是:先移除T的引用属性,得到其底层的原始类型,再给这个原始类型加上const和引用,这样就能避免引用折叠导致const限定失效。
你可以借助C标准库的std::remove_reference_t(C14及以后可用,C++11可以用std::remove_reference<T>::type)来实现:
修改后的代码如下:
#include <iostream> #include <type_traits> // 必须包含这个头文件来使用类型特性 template<typename T> class A { public: explicit A(T& x) : x_(x){} // 先移除T的引用,再给原始类型加const引用 const std::remove_reference_t<T>& get() { return x_; } private: T x_; }; int main() { int x = 42; // A<int&>(x).get() = 43; // 现在这行会编译失败,因为返回的是const int& std::cout << x << '\n'; }
这样,当T是int&时,std::remove_reference_t<T>会得到int,所以get()的返回类型就变成了const int&——这是真正的const引用,不允许对其返回值进行赋值操作,完美达到了你的需求。
你也可以用类型别名让代码更清晰:
template<typename T> class A { // 定义底层类型别名 using underlying_type = std::remove_reference_t<T>; public: explicit A(T& x) : x_(x){} const underlying_type& get() { return x_; } private: T x_; };
内容的提问来源于stack exchange,提问作者Jonathan Boccara

