PAYATHON 2026

C++ 返回类型上的 const 是否无效?为何编译器行为不同?

数据小许

结论

按照 C++ 类型规则,原例中 f<int> 的返回类型是 const int。因此,const int(*x)(int) = f; 应当成立,int(*y)(int) = f; 应当被拒绝。返回类型是函数类型的一部分,匹配函数指针时也要考虑它。标准草案:函数类型、标准草案:获取重载函数地址

题述 GCC、Clang 和 EDG 的接受与拒绝行为符合这些规则。Clang 和 EDG 发出警告,并不意味着这两种函数指针类型相同。题述 MSVC 的处理存在标准符合性问题;由于没有给出版本和编译选项,这一判断只针对题中描述的表现。

为什么 const 保留在返回类型中

原代码使用了 C++20 的简写函数模板语法:

const auto f(auto) { return 1; }

可以将它理解为以下形式:

template<class T>
const auto f(T) { return 1; }

根据 return 1;,返回类型中的 auto 推导为 int。虽然返回的是整数,声明中显式写出的 const 仍会保留,因此完整返回类型是 const int。标准草案:占位类型推导

取 f<int> 的地址,得到的指针类型是:

const int (*)(int)

它与 int (*)(int) 是不同的类型,也不能通过普通隐式转换去掉函数返回类型中的 const。

为什么警告说 const 没有作用

函数的返回类型和调用表达式的类型需要分开看。

对于非类、非数组类型的 prvalue,标准要求在进一步分析表达式之前移除其顶层 cv 限定。因此,f<int> 的返回类型虽然是 const int,调用表达式 f(0) 的类型却是 int。标准草案:表达式类型调整

以下断言给出了这两条规则的预期结果:

#include <type_traits>

const auto f(auto) { return 1; }

// 函数指针保留返回类型中的 const。
static_assert(std::is_same_v<
    decltype(&f<int>),
    const int (*)(int)
>);

// 调用产生的标量 prvalue 不保留顶层 const。
static_assert(std::is_same_v<decltype(f(0)), int>);

// 两种函数指针类型不同。
static_assert(!std::is_same_v<
    const int (*)(int),
    int (*)(int)
>);

警告的意思是,对于普通整数的调用结果,这个 const 没有提供额外的只读保护。不能由此推断它不参与函数类型。

实际代码应该怎样写

如果函数只是按值返回整数,通常可以直接去掉返回类型上的 const:

auto f(auto) { return 1; }

int (*p)(int) = f;

如果需要保留原有函数声明,函数指针就应与它的类型匹配:

const auto f(auto) { return 1; }

const int (*p)(int) = f;

如果外部接口固定要求 int (*)(int),可以增加一个返回 int 的适配函数。无捕获 lambda 也可以完成适配:

const auto f(auto) { return 1; }

int (*callback)(int) = +[](int value) -> int {
    return f(value);
};

这样创建了一个返回类型匹配的新函数入口。不要通过强制转换函数指针来掩盖类型差异。

注意适用范围

上述对调用表达式去除顶层 const 的解释适用于这里的标量按值返回。返回 const T&、返回 const T*,以及按值返回带 const 的类对象,都需要分别分析,不能直接套用整数示例的结论。

另外,形成函数类型时会移除按值参数的顶层 cv 限定。这是针对参数的规则,不能用来解释或删除返回类型上的 const。标准草案:函数参数类型调整

备注:内容仅供参考。