【问题标题】:mixing use of constexpr and const?混合使用 constexpr 和 const?
【发布时间】:2011-02-15 01:57:16
【问题描述】:

我读了一点标准库的 CLang 实现,它让我对 const 和 constexpr 有点困惑。

template<class _Tp, _Tp __v>
struct integral_constant
{
    static constexpr _Tp value = __v;
};

template<class _Tp, _Tp __v>
const _Tp integral_constant<_Tp, __v>::value;

让我感到困惑的是,它在类定义内部使用 constexpr 而在外部使用 const。我的问题是,这是否允许?在什么情况下 const 和 constexpr 可以互换使用?当然 constexpr 函数不能适用于 const,所以我说的是 const 数据和 constexpr 数据。

我确实阅读了一些标准草案和提案 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf, 但这让我感到更加困惑。所以我还有一些问题,

在N2235中,明确指出,const数据不保证是编译时常量,见下例,

struct S {
    static const int size;
};
const int limit = 2 * S::size; // dynamic initialization
const int S::size = 256;

而 constexpr 应该可以解决这个问题,所以至少在这种情况下,constexpr 是不允许的,如下所示,

struct S {
    static const int size;
};
constexpr int limit = 2 * S::size; // shall be error in my understanding
const int S::size = 256;

但是,在阅读了 C++ 标准草案 N3225 之后,我没有看到任何地方明确指出上述示例会导致错误。特别是从 7.1.5/9 开始,

一个 constexpr 说明符用于 对象声明声明对象 作为常量。这样的对象应具有 文字类型并且应该被初始化。 如果由构造函数初始化 调用,构造函数应为 constexpr 构造函数和每个 构造函数的参数应该是 常量表达式。该电话应 是一个常数表达式(5.19)。 否则,每一个完整的表达式 出现在其初始化程序中的应为 常量表达式。

因此,如果 constexpr int limit = 2 * S::size;是无效的,那么 S::size 一定不是常量表达式,那么从 5.19(常量表达式)开始,我看不到标准不允许上例中的 2 * S::size 不是常量表达式。

谁能指出我忽略了什么?非常感谢。

【问题讨论】:

    标签: c++ c++11 constants clang


    【解决方案1】:

    S::size 不是根据 N3225 §5.19p2 的常量表达式:

    条件表达式是一个常量表达式,除非它涉及以下之一……

    • 左值到右值的转换 (4.1),除非它应用于
      • 整数或枚举类型的左值,它引用具有先前初始化、使用常量表达式初始化的非易失性 const 对象,或
      • [其他不适用的条件]

    请注意,我引用的第二个要点如何允许本身使用常量表达式初始化的整体静态数据成员也是常量表达式,但您的 S::size 未初始化。

    (旁注:常量表达式是根据条件表达式定义的,因为这就是 C++ 语法的工作方式。)

    如果您想知道左值到右值的转换是如何发生的,请参阅 §5p9:

    当一个泛左值表达式作为一个操作数的操作数出现时,该操作数需要一个纯右值,左值到右值 (4.1)、数组到指针 (4.2) 或函数到指针 (4.3)应用标准转换将表达式转换为纯右值。

    这可能是如何阅读标准 doesn't make a good reference 的一个很好的例子,尽管 0x 还没有太多其他可用的。

    【讨论】:

    • 嗨,谢谢。我忽略了它。但是,虽然我确实在网上发现很多人都这么说,但我仍然没有找到提到的标准中的哪里:赋值运算符期望右侧是右值,二元算术运算符期望它们的两个操作数都是右值。
    • 这绝对是正确的规则,但问题不在于S::size 没有用常量表达式初始化,因为它的定义确实有这样的初始化。问题在于顺序:该规则需要 preceding 初始化,而这里的定义和初始化遵循尝试的用法。
    【解决方案2】:

    “出现在它的初始化器中的每个完整表达式都应该是一个常量表达式”

    S::size不是常量表达式,因此不能出现在常量表达式的初始化中。

    【讨论】:

    • 我的问题实际上是:为什么 S::size 不是一个常量表达式? 5.19(常量表达式)看了好几遍,没发现里面提到S::size不是常量表达式的情况。
    猜你喜欢
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    • 2023-04-08
    • 2015-05-04
    • 2023-03-15
    • 2015-07-02
    • 1970-01-01
    相关资源
    最近更新 更多