Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++1y/C++14: Assignment to object outside its lifetime is not allowed in a constant expression?

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?

like image 418
Andrew Tomazos Avatar asked Feb 16 '14 01:02

Andrew Tomazos


People also ask

What does do not access an object outside of its lifetime?

Every object has a lifetime in which it can be used in a well-defined manner. The lifetime of an object begins when sufficient, properly aligned storage has been obtained for it and its initialization is complete.

What is const expression c++?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

What is a constant expression?

A constant expression is an expression that can be evaluated at compile time. Constants of integral or enumerated type are required in several different situations, such as array bounds, enumerator values, and case labels. Null pointer constants are a special case of integral constants.


1 Answers

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.

like image 86
Shafik Yaghmour Avatar answered Oct 24 '22 20:10

Shafik Yaghmour