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)?

1
  • 2
    g++ relaxes the requirements on constexpr in g++ version 5, so maybe that's the reason. clang has implemented N3638 in version 3.4 already. Commented Feb 10, 2015 at 16:11

2 Answers 2

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).

Sign up to request clarification or add additional context in comments.

2 Comments

The problematic part is the function pointer gen, not the array constructor.
@SebastianRedl why would that be problematic?
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.

3 Comments

The invocation addressed by this bullet point can be implicit. That said I believe the call should be fine.
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. I suspect this is a g++ bug.
See my answer for a Standard quote why there should be no doubt that everything in the OP's code is bona fide constexpr.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.