为什么从未被检测为常量的函数返回常量?

Why is returning a const from a function not being detected as a const?

我有一个程序依赖于 std::is_same_v <const value_t, decltype(value)> 的结果。但是,我发现当函数被传递给这个表达式时,结果是出乎意料的,导致我出现错误。

我认为函数 returning const value_t 将被视为与 const value_t 相同,但情况似乎并非如此,因为 std::is_same_v <value_t, decltype(func())> return是真的。

我尝试使用 std::as_const, using static_cast, returning it from a constexpr 函数 return 处理这些值,但其中 none 按预期工作。

一个最小的、可重现的例子:

#include <type_traits>
#include <iostream>
   
inline const int x = 1;
/// A constant integer variable.
   
inline const int y() {return x;}
/// A constant integer function returning <x>.
   
int main()
{
    /// <x> successfully passes as being a constant.
    std::cout << std::is_same_v <const int, decltype(x)> << " ";
   
    /// But returning it from a function (<y>) does not.
    std::cout << std::is_same_v <const int, decltype(y())> << std::endl;
}                     

为什么会这样?如何确保 std::is_same_v <const value_t, decltype(value)>std::is_same_v <const value_t, decltype(func())> 都 return 为真?

y() 是一个纯右值表达式。这个表达式的类型不是const int,而是int.

这是因为纯右值类型 non-class non-array 表达式的 cv-qualifiers 已被剥离。

换句话说,如果您使用 class 类型,它会起作用,但不适用于 non-class 类型。

这就是语言的工作原理。 const intint 纯右值之间没有区别,其他基本类型也类似。它们只是限定符没有用处的值。

相反,表达式 x 是一个左值,而不是纯右值,因此 cv-qualifier 没有被剥离。通过 const 限定和非 const 限定左值引用对象之间存在差异。

但即便如此,直接应用于 non-parenthesized 名称的 decltype 实际上并不考虑表达式的类型,而是考虑命名实体声明的类型。这是一个特例。 decltype((x)) 会考虑表达式类型并产生 const int&,添加左值引用,因为 x 是左值。

std::invoke_result 也被指定为 return INVOKE 表达式的 decltype,所以它也会有同样的问题。


您可以从函数类型的 return 类型中获取 const 限定类型。一种典型的方法是基于函数类型的部分专业化。不幸的是,正确地做到这一点非常麻烦,因为必须写出很多专业化来涵盖所有情况。如果 y 过载或通用 lambda/functor.

它也不会工作

我的猜测是,例如boost::callable_traits::return_type 以这种方式实现,将产生 const 合格的 int.

它产生了预期的结果(参见 https://godbolt.org/z/7fYn4q9vs):

#include <type_traits>
#include <iostream>
#include <boost/callable_traits/return_type.hpp>
   
inline const int x = 1;
/// A constant integer variable.
   
inline const int y() {return x;}
/// A constant integer function returning <x>.
   
int main()
{
    /// <x> successfully passes as being a constant.
    std::cout << std::is_same_v <const int, decltype(x)> << " ";
   
    /// And returning it from a function (<y>) now does as well.
    std::cout << std::is_same_v <const int, boost::callable_traits::return_type_t<decltype(y)>> << std::endl;
}

这是因为 expr#6 指出:

If a prvalue initially has the type cv T, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.

这意味着在您的特定示例中,由于调用表达式 y() 是类型为 const intprvalue,因此上面的引用适用于它因此在进一步分析之前它将被调整为 int 因为 int 是一个内置类型而不是 class 类型因此在这种情况下结果是 false.


另一方面,decltype(x) 给出了 x 声明类型 ,它是 const int 而不是 int,因此在这种情况下,结果是 true

请注意,即使您要写 decltype((x)),结果也将是 true,因为当 x 用作表达式时,它是一个 左值 所以上面的引用不适用于它(并且没有对 const int 进行调整)。