如果基类包含数组成员,则派生类的构造函数不能是constexpr

constructor of derived class cannot be constexpr if base class contains array member

本文关键字:构造函数 不能 constexpr 派生 包含数 基类 组成员 如果      更新时间:2023-10-16

我想定义派生类型(SBar)的constexpr值,使用一个构造函数,该构造函数的唯一参数是基类(SFoo)的变量,它只用于初始化基类。

当基类没有数组成员时,这种方法可以很好地工作。但是,当我添加一个数组时,派生值不能再是constexpr。不过,基类的简单副本确实会产生constexpr结果。

为了安全起见,我明确默认了所有复制和移动构造函数。

test.cpp

#define USE_ARRAY

struct SFoo
    {
        constexpr SFoo() =default;
        constexpr SFoo(SFoo const&) =default;
        constexpr SFoo(SFoo      &) =default;
        constexpr SFoo(SFoo     &&) =default;
        constexpr SFoo& operator = (SFoo const&) =default;
        constexpr SFoo& operator = (SFoo      &) =default;
        constexpr SFoo& operator = (SFoo     &&) =default;
#       ifdef USE_ARRAY
            constexpr SFoo(int const (&array)[1]) :
                M_array{array[0]}
                {}
            int M_array[1] = {0};
#       else
            constexpr SFoo(int value) :
                M_value{value}
                {}
            int M_value = 0;
#       endif
    };

struct SBar : SFoo
    {
        constexpr SBar() =default;
        constexpr SBar(SBar const&) =default;
        constexpr SBar(SBar      &) =default;
        constexpr SBar(SBar     &&) =default;
        constexpr SBar& operator = (SBar const&) =default;
        constexpr SBar& operator = (SBar      &) =default;
        constexpr SBar& operator = (SBar     &&) =default;
        constexpr SBar(SFoo foo) : SFoo(foo) {}
    };

// Instances:
#       ifdef USE_ARRAY
    constexpr int arg[1] = {3};
#       else
    constexpr int arg = 3;
#       endif
constexpr SFoo foo(arg); // base "value" constructor is constexpr.
constexpr SFoo foo2(foo); // base copy constructor is constexpr.
constexpr SBar bar(foo); // (line 54): this line fails.

用编译

clang++ -std=c++1z -c -o test.o test.cpp 

产生

test.cpp:54:16: error: constexpr variable 'bar' must be initialized by a constant expression
constexpr SBar bar(foo);
               ^~~~~~~~
1 error generated.

然而,如果我不定义USE_ARRAY,一切都会正常工作。

有人知道为什么会发生这种事吗?

(我知道std::array会有所帮助,但我更愿意使用本机数组并了解潜在的问题)。

因此,对于clang,似乎有几个修复方法。您可以更改:

constexpr SBar(SFoo foo) : SFoo(foo) {}

通过常量引用获取foo

constexpr SBar(const SFoo &info) : SFoo(info) {}

另一个似乎有效的修复方法是注释掉sFoo中的以下副本构造函数:

//constexpr SFoo(SFoo      &) =default;

我没有立即看到C++1z标准草案中的语言使这一改变有意义。

另一方面,gcc抱怨复制构造函数说隐式定义不是constexpr(请参阅它的live),例如:

error: explicitly defaulted function 'constexpr SFoo& SFoo::operator=(const SFoo&)' cannot be declared as constexpr because the implicit declaration is not constexpr
    constexpr SFoo& operator = (SFoo const&) =default;
                    ^

这在我阅读7.1.5[dcl.constexpr]5.20[Epr.const]时并不明显

根据我对12.8p26节的阅读,隐式定义的复制/移动赋值应该是constexpr。所以gcc在这里似乎不正确。

这不是因为数组成员。这是由于赋值运算符。如果您进行以下更改,代码将同时适用于CLANG和GCC:

struct SFoo {
  constexpr SFoo() = default;
  constexpr SFoo(SFoo const&) = default;
  constexpr SFoo(SFoo&&) = default;
  constexpr SFoo(int const (&array)[1]) : M_array{array[0]} {}
  int M_array[1] = {0};
};
struct SBar : SFoo {
  constexpr SBar() = default;
  constexpr SBar(SBar const&) = default;
  constexpr SBar(SBar&&) = default;
  constexpr SBar(SFoo info) : SFoo(info) {}
};

实时演示

如果您使用std::array:而不是传统阵列,则先前的设置将起作用

struct SFoo {
  constexpr SFoo() = default;
  constexpr SFoo(SFoo const&) = default;
  constexpr SFoo(SFoo      &) = default;
  constexpr SFoo(SFoo     &&) = default;
  constexpr SFoo& operator = (SFoo const&) = default;
  constexpr SFoo& operator = (SFoo      &) = default;
  constexpr SFoo& operator = (SFoo     &&) = default;
  constexpr SFoo(std::array<int, 1> const &array) : M_array{array} {}
  std::array<int, 1> M_array = {};
};

实时演示

没有原因。我还在找。。。