6

Writting a simple compile time std::array factory from a generator function, I stumbled upon this: clang++ 3.5.1 and g++ 4.9.2 disagree on whether a function is constexpr or not.

The code (this is c++14!):

#include <array>
#include <utility>

    template <class T, std::size_t N, class GenType, std::size_t... I> 
    constexpr std::array<T, N>
make_array_impl (GenType gen, std::index_sequence <I...>) 
{
    return {{ gen (I)... }};
}

    template <class T, std::size_t N, class GenType> 
    constexpr std::array<T, N>
make_array (GenType gen)
{
    return make_array_impl <T, N> (
            gen, 
            std::make_index_sequence <N> {}
    );
}

    constexpr int
generator_const (std::size_t /* index */)
{
    return 1;
}

    constexpr auto
a = make_array <int, 3> (generator_const);

static_assert (a.size () == 3, "");
static_assert (a[0] == 1, "");
static_assert (a[1] == 1, "");
static_assert (a[2] == 1, "");

int main () {}

Compiling with g++:

migou ~ % g++ -std=c++14 ex.cpp  
ex.cpp:28:41:   in constexpr expansion of ‘make_array<int, 3ul, int (*)(long unsigned int)>(generator_const)’
ex.cpp:18:5:   in constexpr expansion of ‘make_array_impl<int, 3ul, int (*)(long unsigned int), {0ul, 1ul, 2ul}>(gen, (std::make_index_sequence<3ul>{}, std::make_index_sequence<3ul>()))’
ex.cpp:8:21: error: expression ‘generator_const’ does not designate a constexpr function
 return {{ gen (I)... }};

With clang++ it compiles just fine. Can I go on and consider this valid g++14 (and thus g++ bugged)?

Paul R
  • 208,748
  • 37
  • 389
  • 560
minusplus
  • 63
  • 5
  • 2
    `g++` relaxes the requirements on `constexpr` in [`g++` version __5__](https://gcc.gnu.org/projects/cxx1y.html), so maybe that's the reason. `clang` has implemented N3638 in version 3.4 already. – Zeta Feb 10 '15 at 16:11

2 Answers2

2

As @Casey correctly pointed out in the comments, there is nothing foggy about the constexpr-ness of the implicit constructor of std::array or other aggregates:

12.1 Constructors [class.ctor]

5 A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8) or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2) and an empty compound-statement. If that user-written default constructor would be ill-formed, the program is ill-formed. If that user-written default constructor would satisfy the requirements of a constexpr constructor (7.1.5), the implicitly-defined default constructor is constexpr. Before the defaulted default constructor for a class is implicitly defined, all the non-user-provided default constructors for its base classes and its nonstatic data members shall have been implicitly defined. [ Note: An implicitly-declared default constructor has an exception-specification (15.4). An explicitly-defaulted definition might have an implicit exceptionspecification, see 8.4. —end note ]

This has been fixed in the latest gcc HEAD 5.0.0 20150217, see this live example, and has been working in Clang since almost a year and a half now (since the 3.4 release IIRC, see this Q&A).

Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
1

This is kinda foggy. The rules for constexpr in C++14 forbid (N3797, 5.19/2 bullet 2)

an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor

constexpr is not part of the type, so the function pointer passed to make_array_impl is not a constexpr function. On the other hand, it refers to a constexpr function, and since this is constexpr evaluation, the compiler has to know that.

However, Clang supports that code, and GCC 4.9 doesn't claim conformance with relaxed constexpr functions, so I would trust Clang in this case.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • The invocation addressed by this bullet point can be implicit. That said I believe the call should be fine. – Columbo Feb 10 '15 at 16:58
  • I don't think support for relaxed `constexpr` is an issue here. The function invoked *is* a `constexpr` function, so this should be perfectly conforming to even the C++11 `constexpr` rules. Indeed, [both clang and g++ compile this simplified test case in C++11 mode without any diagnostics](http://coliru.stacked-crooked.com/a/85af35047baa4386). I suspect this is a g++ bug. – Casey Feb 10 '15 at 19:45
  • See my answer for a Standard quote why there should be no doubt that everything in the OP's code is bona fide `constexpr`. – TemplateRex Feb 18 '15 at 12:19