gcc和clang在constexpr函数上存在分歧

gcc and clang disagree over constexpr function

本文关键字:存在 函数 constexpr clang gcc      更新时间:2023-10-16

从生成器函数中编写一个简单的编译时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++bug)吗?

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

12.1建设者[class.ctor]

5默认构造函数,它是默认的,未定义为已删除是在odr使用(3.2)创建的对象时隐式定义的其类类型(1.8),或者在其第一次声明。隐式定义的默认构造函数执行将由用户为该类编写的默认构造函数ctor初始值设定项(12.6.2)和一个空的复合语句。如果用户编写的默认构造函数格式不正确,程序为不正规如果用户编写的默认构造函数满足constexpr构造函数(7.1.5)的要求隐式定义的默认构造函数是constexpr在类的默认默认构造函数是隐式定义的,所有非用户为其基类提供的默认构造函数,以及其非静态数据成员应已隐式定义。[注:隐式声明的默认构造函数具有异常规范(15.4)。显式默认定义可能有隐含的例外说明,请参见8.4--尾注]

这已经在最新的gcc HEAD 5.0.0 20150217中修复,请参阅此实时示例,并且已经在Clang工作了近一年半(自3.4版本IIRC以来,请参阅此问答)。

这里有点雾蒙蒙的。C++14中constexpr的规则禁止(N3797,5.19/2子弹头2)

调用文字类的constexpr构造函数以外的函数、constexpr函数,或隐式调用平凡的析构函数

constexpr不是类型的一部分,因此传递给make_array_impl的函数指针不是constexpr函数。另一方面,它引用到constexpr函数,由于这是constexpr求值,编译器必须知道这一点。

然而,Clang支持该代码,并且GCC 4.9没有声称与宽松的constexpr函数一致,所以在这种情况下我会信任Clang。