我想出了这堂课:
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++;
}
};
这是用法:
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)
}
问题:
GetX
与X+Z
编译良好作为返回表达式(Z不是constexpr)。FoolConst
对象(GetY
)中调用constexpr
和pt
方法? GetX
中main
的行为在编译器中有所不同。 MSVC使用int
作为模板参数进行编译,而GCC(IdeOne)不会编译它。对于一个编译器constexpr GetX
是真正的constexpr
,但对于其他编译器,如果涉及X+Z
则不是。如果我删除+Z
,只需return X
GCC就可以了。
我的问题非常基础:如果对象是constexpr
,它怎么能调用非constexpr方法?
答案 0 :(得分:4)
常量表达式无法访问可变子对象。这是在 [expr.const] / 2:
中条件表达式
e
是一个核心常量表达式,除非e的评估遵循规则 抽象机器(1.9),将评估以下表达式之一:[...]
- 左值 - 右值转换(4.1),除非它适用于[...]
- 一个非易失性glvalue,它引用用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),该程序是不正确的;没有 需要诊断。
&#34;第III形成的;无需诊断&#34;另一种说法是你的程序行为未定义。
- 如何从constexpr对象(pt)中调用FoolConst和GetY方法?
那绝对没问题;从该对象的非constexpr
成员函数的角度来看,声明为const
的对象只是一个constexpr
对象。
- GetX在main中的行为在编译器中是不同的。 MSVC使用int作为模板参数进行编译,而GCC(IdeOne)不会编译它。
不幸的是,两个编译器都是正确的;您的程序在GetX
的定义中有未定义的行为,因此编译器没有单一的正确行为。
答案 1 :(得分:2)
- const对象是const T类型的对象或这种对象的非可变子对象。
因此,在你的例子中,Z是非常量的,因此不能用于常量表达式,就像GCC所说:
error: mutable 'Point::Z' is not usable in a constant expression
答案 2 :(得分:1)
数目:
为什么
GetX
与X+Y
汇编得很好,因为返回表达式(Z
不是constexpr
)。
constexpr
声明后整个对象是常量。对象的约束总是由所有成员派生,除非声明成员mutable
。如何从
FoolConst
对象(GetY
)中调用constexpr
和pt
方法? main中GetX
的行为在编译器中是不同的。 MSVC使用int作为模板参数进行编译,而GCC(IdeOne)不会编译它。
Z
使用GetX()
,因为使用mutable
字段会破坏constexpr
(OTOH gcc的行为有点有点误导,因为它应该在GetX()
的定义中报告错误:要么必须定义它,要么不能constexpr
,或者在定义时它不能使用可变字段或变量定义在课外)。GetX()
的结果作为模板参数,它引用一个mutable
字段,它肯定会破坏标准,这种行为实际上是未定义的,因为它应该产生无论Z的运行时值发生了什么,都会产生相同的结果(因为它在编译时已经解决了。