C ++ 1y / C ++ 14:在常量表达式中不允许分配给它的生命周期之外的对象? [英] C++1y/C++14: Assignment to object outside its lifetime is not allowed in a constant expression?
问题描述
根据当前草案,以下C ++ 14 / C ++ 1y程序是否生成错误?
include< cstddef>
template< typename T,size_t n>
struct literal_array
{
T data [n];
};
template< typename T,size_t n,size_t m>
constexpr literal_array< T,n + m>运算符+(literal_array< T,n> a,
literal_array< T,m> b)
{
literal_array< T,n + m& X;
for(size_t i = 0; i x.data [i] = a.data [i];
for(size_t i = 0; i x.data [n + i] = b.data [i];
return x;
}
int main()
{
constexpr literal_array< int,3> a = {1,2,3};
constexpr literal_array< int,2> b = {4,5};
constexpr auto c = a + b;
}
ang中继(写作时):
错误:constexpr变量'c'必须由常量表达式初始化
constexpr auto c = a + b;
^ ~~~~~
在生命周期之外的对象在常量表达式中不允许赋值
x.data [i] = a.data [i];
^
在调用'operator +({{1,2,3}},{{4,5}})'
constexpr auto c = a + b;
^
这是什么意思?
程序是病毒,因为您不是初始化 x
,如果您将定义更改为:
literal_array< T,n + m> x = {{0}};
clang
不再抱怨错误。另一种解决方案是创建constexpr consrtuctors
我们可以在标准草案部分 7.1.5
< 的 段落 $ c> constexpr 函数应满足以下
约束:
:
其函数体应为
= delete
,= default
或
复合语句不包含
其中包含此项目符号(强调我):
a定义非文字类型或静态或线程的变量
存储持续时间或未对其执行初始化。
之后,我们有以下示例:
constexpr int uninit(){
int a; //错误:变量未初始化
return a;
}
关于 x
似乎并不在草稿标准。正确的原因,我可以告诉应该是对象未初始化
的行。
关于对象生命周期的标准草案的相关引用将为 3.8
段 >
对象的生命周期是对象的运行时属性。如果一个
对象是类
或聚合类型,并且它的一个成员由除了一个琐碎的默认构造函数之外的
构造函数初始化,则
对象被认为具有非平凡的初始化。 [注意:
通过一个简单的复制/移动构造函数初始化是非常简单的
初始化。 - ] <$ c> T 类型的对象的生命周期开始
,当:
- 获得
T
的正确对齐和大小的存储,
- if该对象具有非平凡的初始化,其初始化完成。
我也使用 std :: is_trivial 检查:
std :: cout<< std :: boolalpha< std :: is_trivial< literal_array< int,3>> :: value< std :: endl;
,结果如 true
。
我提交了错误报告,并且回复包含此语句:
[...]问题是我们还没有实现隐含的规则,这样的函数不能在常量表达式中调用。
< blockquote>Is the following C++14/C++1y program ill-formed according to the current draft?
#include <cstddef> template<typename T, size_t n> struct literal_array { T data[n]; }; template<typename T, size_t n, size_t m> constexpr literal_array<T, n+m> operator+(literal_array<T, n> a, literal_array<T, m> b) { literal_array<T, n+m> x; for (size_t i = 0; i < n; i++) x.data[i] = a.data[i]; for (size_t i = 0; i < m; i++) x.data[n+i] = b.data[i]; return x; } int main() { constexpr literal_array<int, 3> a = { 1, 2, 3 }; constexpr literal_array<int, 2> b = { 4, 5 }; constexpr auto c = a + b; }
Clang trunk (at time of writing) gives:
error: constexpr variable 'c' must be initialized by a constant expression constexpr auto c = a + b; ^ ~~~~~ assignment to object outside its lifetime is not allowed in a constant expression x.data[i] = a.data[i]; ^ in call to 'operator+({{1, 2, 3}}, {{4, 5}})' constexpr auto c = a + b; ^
What does it mean "assignment to object outside its lifetime"? The lifetime of x and its subobjects encloses the function, so what is it on about?
解决方案The program is ill-formed because you are not initializing
x
, if you change the definition to:literal_array<T, n+m> x = {{0}};
clang
no longer complains and it compiles without error. Another solution would be to create constexpr consrtuctors.We can find this in the draft standard section
7.1.5
The constexpr specifier paragraph 3 which says:The definition of a
constexpr
function shall satisfy the following constraints:and includes the following bullet:
its function-body shall be
= delete
,= default
, or a compound-statement that does not containwhich contains this bullet (emphasis mine):
a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
and later on we have the following example:
constexpr int uninit() { int a; // error: variable is uninitialized return a; }
The complaint about the lifetime of
x
does not seem founded in the draft standard. The correct reason as far as I can tell should be something along the lines ofobject is not initialized
.The relevant quote from the draft standard on object lifetime would be section
3.8
Object lifetime paragraph 1 which says:The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [ Note: initialization by a trivial copy/move constructor is non-trivial initialization. — end note ] The lifetime of an object of type
T
begins when:
- storage with the proper alignment and size for type
T
is obtained, and- if the object has non-trivial initialization, its initialization is complete.
Just in case I was missing something I also checked using std::is_trivial:
std::cout << std::boolalpha << std::is_trivial<literal_array<int, 3>>::value << std::endl ;
and the result as expected in
true
,.Update
I filed a bug report for this and the reply includes this statement:
[...]The problem is that we don't yet implement the implied rule that such a function can't be invoked in a constant expression.
这篇关于C ++ 1y / C ++ 14:在常量表达式中不允许分配给它的生命周期之外的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!