constexpr对象与可变成员

Aja*_*jay 6 c++ language-lawyer constexpr c++11 c++14

我想出了这堂课:

class Point
{
public:
    int X, Y;
    mutable int Z;

    constexpr Point(int x, int y) :X (x), Y(y), Z(0)
    { }

    constexpr int GetX() const
    {
        // Z++; // Wont compile, but following expression is valid!
        return X+Z;
    }

    int GetY() const
    {
        Z++;
        return Y;
    }

    void FoolConst() const
    {
        Z++;
    }
};
Run Code Online (Sandbox Code Playgroud)

这是用法:

template<int S>
void foo()
{
    std::cout << S << std::endl;
}

int main()
{   
    constexpr Point pt(10, 20);

    pt.FoolConst();

    char arr[pt.GetX()]; // Both compile, but GCC is using extended `new`

    foo<pt.GetX()>(); // GCC fails, VC compiles

    std::cout << sizeof(arr); // 10 (MSVC), 11 (GCC)
    std::cout << pt.GetX();  // 11 (MSVC), 11(GCC)
}
Run Code Online (Sandbox Code Playgroud)

问题:

  • 为什么GetX编译好与X+Z返回表达式(Z不是constexpr).
  • 如何从object()调用FoolConstGetY方法?constexprpt
  • GetXin 的行为main在编译器中是不同的.MSVC使用intas模板参数进行编译,而GCC(IdeOne)不会编译它.

对于一个编译器constexpr GetX是真的constexpr,但对于其他编译器,如果X+Z涉及的话.如果我删除+Z,只是return XGCC是好的.

我的问题非常基础:如果对象是constexpr如何调用非constexpr方法?

eca*_*mur 5

常量表达式不能访问可变子对象。这是在[expr.const] /2 中:

条件表达式e是核心常量表达式,除非按照抽象机 (1.9) 的规则对 e 的求值会求值以下表达式之一: [...]

  • 左值到右值的转换 (4.1),除非它应用于 [...]
    • 一个非易失性泛左值,它指的是用 constexpr 定义的非易失性对象,或者是指这种对象的非可变子对象 [...]

所以GetX不能在常量表达式中使用,例如作为模板参数foo<pt.GetX()>()

回答您的具体问题:

  • 为什么 GetX 使用 X+Y 作为返回表达式编译得很好(Z 不是 constexpr)。

编译器不需要检查 constexpr 函数(包括成员函数)在定义时是否完全有效,仅在使用时才检查。它确实必须检查一些事情,例如不使用goto [dcl.constexpr] /3,但它不必检查定义访问哪些对象。这是因为是否可以在常量表达式中使用 constexpr 函数取决于其参数的值。

事实上,因为GetX无条件访问Z,您的程序严格按照[dcl.constexpr] /5具有未定义的行为:

对于非模板、非默认的 constexpr 函数或非模板、非默认、非继承的 constexpr 构造函数,如果不存在参数值使得函数或构造函数的调用可以是核心常量的计算子表达式表达式 (5.19),程序格式错误;无需诊断。

“格式错误;不需要诊断”是另一种说法,即您的程序的行为未定义。

  • 如何从 constexpr 对象 (pt) 中调用 FoolConst 和 GetY 方法?

那绝对没问题;从该对象的非成员函数的角度来看,声明的对象constexpr只是一个对象。constconstexpr

  • GetX 在 main 中的行为在编译器中是不同的。MSVC 使用 int 作为模板参数编译得很好,而 GCC (IdeOne) 不会编译它。

不幸的是,两个编译器都是正确的;您的程序在 的定义中有未定义的行为GetX,因此编译器没有单一的正确行为。


Eth*_*ris 2

答案:

为什么使用as return 表达式GetX编译得很好(不是)。X+YZconstexpr

  • 因为整个对象在用 声明后是不变的constexpr。对象的常量性始终由所有成员派生,除非声明该成员mutable

如何调用FoolConstobject ( )GetY之外的方法?main 中的行为在编译器中有所不同。MSVC 使用 int 作为模板参数可以正常编译,而 GCC (IdeOne) 不会编译它。constexprptGetX

  • 它适用于我,使用 gcc 4.8.3。一切都很好,只要你不使用ZinGetX()因为使用mutablefield 会破坏constexpr(OTOH gcc 的行为有点误导,因为它应该报告 的定义中已经存在的错误GetX():要么必须定义它,要么以其他方式不能constexpr,或者定义时不能使用在类外部定义的可变字段或变量)。
  • 如果您有一个编译器可以很好地编译传递GetX()作为模板参数(引用一个mutable字段)的结果,那么它肯定会违反标准,并且这种行为实际上是未定义的,因为它应该产生相同的结果(因为它在编译中解析) time) 无论 Z 的运行时值发生了什么。