gcc 和 clang 不同意 constexpr 函数

gcc and clang disagree over constexpr function

从生成器函数编写一个简单的编译时 std::array 工厂,我偶然发现了这个:clang++ 3.5.1 和 g++ 4.9.2 在函数是否 constexpr 上存在分歧。

代码(这是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 () {}

使用 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)... }};

使用 clang++ 它编译得很好。我可以继续考虑这个有效的 g++14(因此 g++ 有问题)吗?

这里有点雾。 C++14 中 constexpr 的规则禁止(N3797,5.19/2 项目符号 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 不是类型的一部分,因此传递给 make_array_impl 的函数指针不是 constexpr 函数。另一方面,它 引用 一个 constexpr 函数,因为这是 constexpr 评估,编译器必须知道这一点。

但是,Clang 支持该代码,并且 GCC 4.9 未声明与宽松的 constexpr 函数一致,因此在这种情况下我会信任 Clang。

正如@Casey 在评论中正确指出的那样,std::array 或其他聚合的隐式构造函数的 constexpr-ness 没有任何模糊之处:

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 ]

这已在最新的 gcc HEAD 5.0.0 20150217 中修复,请参阅此 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)。