constexpr 和可变成员和隐式复制构造函数
constexpr and mutable member and implicit copy-ctor
以下代码在 clang 7+ 中编译,但在 5 和 6(使用 c++17 和 c++14)中不编译。
clang 5 和 6 的问题似乎是隐式复制构造函数从可变成员 x
读取。
谁能告诉我整个结构是否符合标准 (c++17),或者程序是否格式错误?或者在早期的 clang 版本中可能未实现的关于隐式复制函数的标准是否发生了变化?
struct Foo {
int a;
mutable int x{};
constexpr Foo() : a(0) {}
//constexpr Foo(const Foo& other) : a(other.a) {} // <- with this line it works on Clang 5 & 6, too
};
struct FooFactory {
static constexpr auto create() {
auto f = Foo{};
return f;
}
};
int main() {
constexpr Foo f = FooFactory::create();
++f.x;
}
实时代码here.
代码格式错误。作为参考,这里是简化的代码,在所有编译器上都失败:
struct Foo {
int a;
mutable int x{};
constexpr Foo() : a(0) {}
};
int main() {
constexpr Foo f;
constexpr Foo f1 = f;
}
根据 [expr.const] 7.7,
A variable is usable in constant expressions after its initializing
declaration is encountered if
it is a constexpr variable,
or ... (3.5) a non-mutable
subobject or reference member of any of the above.
这会取消默认复制构造函数的资格。
这是一个格式正确的 C++17 程序。 constexpr 函数当然可以读取(和写入)非常量 变量:
constexpr int f(int i) {
int j=i;
++j;
return i+j; // neither is a constant expression
}
rule 是常量表达式中检查的任何内容都必须是常量 或 其生命周期开始于 during表达式的评估。在您的情况下,create
的 f.x
的生命周期明显开始于常量表达式的求值,即 main
的 初始化 f
。然而,确实没有 Foo
对象可以被不创建该对象的常量表达式复制,无论它是否是 constexpr
.
唯一的其他候选问题是复制构造函数是否不是 constexpr,但这些要求非常弱。唯一相关的是每个(非变体)成员都被初始化,这肯定是令人满意的,并且它至少可以在一个常量表达式中使用,这已经被证明。
以下代码在 clang 7+ 中编译,但在 5 和 6(使用 c++17 和 c++14)中不编译。
clang 5 和 6 的问题似乎是隐式复制构造函数从可变成员 x
读取。
谁能告诉我整个结构是否符合标准 (c++17),或者程序是否格式错误?或者在早期的 clang 版本中可能未实现的关于隐式复制函数的标准是否发生了变化?
struct Foo {
int a;
mutable int x{};
constexpr Foo() : a(0) {}
//constexpr Foo(const Foo& other) : a(other.a) {} // <- with this line it works on Clang 5 & 6, too
};
struct FooFactory {
static constexpr auto create() {
auto f = Foo{};
return f;
}
};
int main() {
constexpr Foo f = FooFactory::create();
++f.x;
}
实时代码here.
代码格式错误。作为参考,这里是简化的代码,在所有编译器上都失败:
struct Foo {
int a;
mutable int x{};
constexpr Foo() : a(0) {}
};
int main() {
constexpr Foo f;
constexpr Foo f1 = f;
}
根据 [expr.const] 7.7,
A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable,
or ... (3.5) a non-mutable subobject or reference member of any of the above.
这会取消默认复制构造函数的资格。
这是一个格式正确的 C++17 程序。 constexpr 函数当然可以读取(和写入)非常量 变量:
constexpr int f(int i) {
int j=i;
++j;
return i+j; // neither is a constant expression
}
rule 是常量表达式中检查的任何内容都必须是常量 或 其生命周期开始于 during表达式的评估。在您的情况下,create
的 f.x
的生命周期明显开始于常量表达式的求值,即 main
的 初始化 f
。然而,确实没有 Foo
对象可以被不创建该对象的常量表达式复制,无论它是否是 constexpr
.
唯一的其他候选问题是复制构造函数是否不是 constexpr,但这些要求非常弱。唯一相关的是每个(非变体)成员都被初始化,这肯定是令人满意的,并且它至少可以在一个常量表达式中使用,这已经被证明。