具有可变成员的 constexpr 对象
constexpr object with mutable member
I came up with this class:
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++;
}
};
And here is usage:
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)
}
Questions:
- Why
GetX
is compiling well with X+Z
as return expression (Z is not constexpr).
- How can I call
FoolConst
and GetY
methods out of constexpr
object (pt
) ?
- The behaviour of
GetX
in main
is different in compilers. MSVC compiles fine with a int
as template argument, while GCC (IdeOne) won't compile it.
For one compiler constexpr GetX
is truly constexpr
, but for other it is not if X+Z
is involved. If I remove +Z
and simply return X
GCC is okay.
My question is very basic: If object is constexpr
how can it call a non-constexpr method?
海湾合作委员会就在这里。 C++14 标准 [basic.type.qualifier]:
— A const object is an object of type const T or a non-mutable subobject of such an object.
因此,在您的示例中,Z 是非常量,因此不能在常量表达式中使用,就像 GCC 所说:
error: mutable 'Point::Z' is not usable in a constant expression
常量表达式不能访问可变子对象。这是在 [expr.const]/2:
A conditional-expression e
is a core constant expression unless the evaluation of e, following the rules of the
abstract machine (1.9), would evaluate one of the following expressions: [...]
- an lvalue-to-rvalue conversion (4.1) unless it is applied to [...]
- a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers
to a non-mutable sub-object of such an object [...]
因此 GetX
不能在常量表达式中使用,例如作为模板参数 foo<pt.GetX()>()
.
回答您的具体问题:
- Why GetX is compiling well with X+Y as return expression (Z is not constexpr).
编译器不需要检查 constexpr 函数(包括成员函数)在定义时是否完全有效,只有在使用。它确实需要检查一些事情,比如不使用 goto
[dcl.constexpr]/3,但它不必检查定义访问了哪些对象.这是因为 constexpr 函数是否可以在常量表达式中使用取决于其参数的值。
事实上,因为 GetX
无条件访问 Z
,根据 [dcl.constexpr]/5,您的程序严格具有未定义的行为:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting
constexpr constructor, if no argument values exist such that an invocation of the function or constructor
could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no
diagnostic required.
"Ill-formed; no diagnostic required" 是另一种表示程序行为未定义的方式。
- How can I call FoolConst and GetY methods out of constexpr object (pt) ?
那太好了;从该对象的非 constexpr
成员函数的角度来看,声明 constexpr
的对象只是一个 const
对象。
- The behaviour of GetX in main is different in compilers. MSVC compiles fine with a int as template argument, while GCC (IdeOne) won't compile it.
不幸的是,两个编译器都是正确的;你的程序在 GetX
的定义中有未定义的行为,所以编译器没有一个正确的行为。
答案:
Why GetX
is compiling well with X+Y
as return expression (Z
is not constexpr
).
- 因为整个对象在用
constexpr
声明后是常量。对象的常量性始终由所有成员派生,除非成员被声明为 mutable
.
How can I call FoolConst
and GetY
methods out of constexpr
object (pt
) ?
The behaviour of GetX
in main is different in compilers. MSVC compiles fine with a int as template argument, while GCC (IdeOne) won't compile it.
- 它适用于我,gcc 4.8.3。一切都很好,只要你不在
GetX()
中使用 Z
因为 mutable
字段的使用会破坏 constexpr
(OTOH gcc 的行为有点误导因为它应该报告 GetX()
定义中已经存在的错误:要么必须定义它,要么不能定义 constexpr
,或者在定义时它不能使用在 [= 之外定义的可变字段或变量43=]).
- 如果你有一个编译器可以很好地传递
GetX()
的结果作为模板参数,它引用 mutable
字段,它肯定会违反标准,并且这种行为实际上是未定义的,因为它无论 Z 的运行时值发生什么变化,都应该产生相同的结果(因为它是在编译时解决的)。
I came up with this class:
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++;
}
};
And here is usage:
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)
}
Questions:
- Why
GetX
is compiling well withX+Z
as return expression (Z is not constexpr). - How can I call
FoolConst
andGetY
methods out ofconstexpr
object (pt
) ? - The behaviour of
GetX
inmain
is different in compilers. MSVC compiles fine with aint
as template argument, while GCC (IdeOne) won't compile it.
For one compiler constexpr GetX
is truly constexpr
, but for other it is not if X+Z
is involved. If I remove +Z
and simply return X
GCC is okay.
My question is very basic: If object is constexpr
how can it call a non-constexpr method?
海湾合作委员会就在这里。 C++14 标准 [basic.type.qualifier]:
— A const object is an object of type const T or a non-mutable subobject of such an object.
因此,在您的示例中,Z 是非常量,因此不能在常量表达式中使用,就像 GCC 所说:
error: mutable 'Point::Z' is not usable in a constant expression
常量表达式不能访问可变子对象。这是在 [expr.const]/2:
A conditional-expression
e
is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions: [...]
- an lvalue-to-rvalue conversion (4.1) unless it is applied to [...]
- a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object [...]
因此 GetX
不能在常量表达式中使用,例如作为模板参数 foo<pt.GetX()>()
.
回答您的具体问题:
- Why GetX is compiling well with X+Y as return expression (Z is not constexpr).
编译器不需要检查 constexpr 函数(包括成员函数)在定义时是否完全有效,只有在使用。它确实需要检查一些事情,比如不使用 goto
[dcl.constexpr]/3,但它不必检查定义访问了哪些对象.这是因为 constexpr 函数是否可以在常量表达式中使用取决于其参数的值。
事实上,因为 GetX
无条件访问 Z
,根据 [dcl.constexpr]/5,您的程序严格具有未定义的行为:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.
"Ill-formed; no diagnostic required" 是另一种表示程序行为未定义的方式。
- How can I call FoolConst and GetY methods out of constexpr object (pt) ?
那太好了;从该对象的非 constexpr
成员函数的角度来看,声明 constexpr
的对象只是一个 const
对象。
- The behaviour of GetX in main is different in compilers. MSVC compiles fine with a int as template argument, while GCC (IdeOne) won't compile it.
不幸的是,两个编译器都是正确的;你的程序在 GetX
的定义中有未定义的行为,所以编译器没有一个正确的行为。
答案:
Why
GetX
is compiling well withX+Y
as return expression (Z
is notconstexpr
).
- 因为整个对象在用
constexpr
声明后是常量。对象的常量性始终由所有成员派生,除非成员被声明为mutable
.
How can I call
FoolConst
andGetY
methods out ofconstexpr
object (pt
) ? The behaviour ofGetX
in main is different in compilers. MSVC compiles fine with a int as template argument, while GCC (IdeOne) won't compile it.
- 它适用于我,gcc 4.8.3。一切都很好,只要你不在
GetX()
中使用Z
因为mutable
字段的使用会破坏constexpr
(OTOH gcc 的行为有点误导因为它应该报告GetX()
定义中已经存在的错误:要么必须定义它,要么不能定义constexpr
,或者在定义时它不能使用在 [= 之外定义的可变字段或变量43=]). - 如果你有一个编译器可以很好地传递
GetX()
的结果作为模板参数,它引用mutable
字段,它肯定会违反标准,并且这种行为实际上是未定义的,因为它无论 Z 的运行时值发生什么变化,都应该产生相同的结果(因为它是在编译时解决的)。