7

I have the code below, that basically maps an std::integer_sequence<> into an std::array<> at compile time:

#include <iostream>
#include <utility>
#include <array>

template<int...Is>
constexpr auto make_array(const std::integer_sequence<int, Is...>& param) // this works */
// constexpr auto make_array(std::integer_sequence<int, Is...> param) // doesn't compile
{
    return std::array<int, sizeof...(Is)> {Is...};
}

int main()
{
    constexpr std::integer_sequence<int, 1,2,3,4> iseq;

    // If I pass by value, error: the value of 'iseq' is not usable in a constant expression
    constexpr auto arr = make_array(iseq);  

    for(auto elem: arr)
        std::cout << elem << " ";
}

The code works fine whenever make_array takes its argument by const-reference. Whenever I try passing it by value, like in the commented line, it spits an error:

error: the value of 'iseq' is not usable in a constant expression

    constexpr auto arr = make_array(iseq);  

Why is this? The parameter iseq is certainly a constant expression, why cannot I pass it to make_array?

For example, the code below works as expected when passing by value:

#include <iostream>
#include <utility>

struct Foo
{
    int _m;
    constexpr Foo(int m): _m(m){};
};

constexpr Foo factory_foo(int m)
{
    return Foo{m};
}

constexpr Foo copy_foo(Foo foo)
{
    return foo;
}

int main()
{
    constexpr Foo cxfoo = factory_foo(42);
    constexpr Foo cpfoo = copy_foo(cxfoo);
}

EDIT

I'm using g++5.1 from macports. Using clang++ 3.5, I get an error message even for the code that compiles with g++ (with const reference):

error: default initialization of an object of const type 'const std::integer_sequence' requires a user-provided default constructor

so I guess there is some issue with the lack of a user-provided default constructor, but at this point I don't really understand what's going on.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Which compiler(s) and version(s). – Shafik Yaghmour May 11 '15 at 16:09
  • @ShafikYaghmour g++5.1, will try on clang soon. See the updated edit, clang++ is spitting an error even in the case of passing by `const` reference. I am probably missing something about default-ctors in constant expressions. – vsoftco May 11 '15 at 16:11

2 Answers2

6

If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

However, integer_sequence does not have any user-provided constructors, and constexpr implies const for variables, so you can't define a constexpr object of that type without an initializer.
Adding an initializer makes it compile on Clang.

Columbo
  • 60,038
  • 8
  • 155
  • 203
5

You are missing an initializer on iseq. You have to add it:

constexpr std::integer_sequence<int, 1,2,3,4> iseq{};
                                                  ^^

From [dcl.constexpr]:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.20). Otherwise, or if a constexpr specifier is used in a reference declaration, every fullexpression that appears in its initializer shall be a constant expression. [ Note: Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization is part of such a full-expression. —end note ]
[ Example:

struct pixel {
    int x, y;
};
constexpr pixel ur = { 1294, 1024 };  // OK
constexpr pixel origin;               // error: initializer missing

—end example ]

Moreover, as Columbo suggests in his comment and answer, simply being initialized is insufficent. A user-provided constructor is also required, as per [dcl.init]:

If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

It's a little odd to have the most relevant section (dcl.constexpr) have an incomplete description of the requirements for a constepxr object declaration.

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Darn!!!! That was an easy fix :) Then I guess g++ has a bug. It didn't even cross my mind to check only the definition of `iseq`, for which clang++ would've spit an error. – vsoftco May 11 '15 at 16:20
  • Saw that quote when I checked about the implicit const thing, +1 – Columbo May 11 '15 at 16:24
  • @Columbo will report the bug for g++ (if it's not already reported) – vsoftco May 11 '15 at 16:31
  • @vsoftco Check with HEAD on Wandbox first. – Columbo May 11 '15 at 16:34
  • @Barry IMO Your quote is not sufficient. Consider http://coliru.stacked-crooked.com/a/18119a57aa765be3 where arguably `a` is initialized, internally, but no user-provided constructor is present, which makes it ill-formed. :) – Columbo May 11 '15 at 16:36
  • @Columbo compiles with HEAD gcc6 – vsoftco May 11 '15 at 16:40
  • @Columbo That's a good point. I'd call it a wording defect. "Shall be initialized" is incomplete for the intent... – Barry May 11 '15 at 16:45
  • @Barry I wouldn't necessarily call this a defect, since the second quote you provided completes it (initializing and providing a user-provided ctor are orthogonal things). Maybe referring to [dcl.init] in a note would be appropriate. – Columbo May 11 '15 at 16:50
  • @Columbo Less defect, more "sub-optimal word choice"? – Barry May 11 '15 at 16:54
  • Filled the bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66109, provided the wrong link before :) – vsoftco May 11 '15 at 16:55
  • 2
    gcc is probably allowing this [due to dr 253](http://stackoverflow.com/a/29683948/1708801) – Shafik Yaghmour May 11 '15 at 17:09