C ++ 1y / C ++ 14:在常量表达式中不允许分配给它的生命周期之外的对象? [英] C++1y/C++14: Assignment to object outside its lifetime is not allowed in a constant expression?

查看:244
本文介绍了C ++ 1y / C ++ 14:在常量表达式中不允许分配给它的生命周期之外的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据当前草案,以下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 contain

which 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 of object 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆