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

时间:2015-12-04 20:43:14

标签: c++ arrays inheritance constexpr c++17

我想使用构造函数定义派生类型(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可以提供帮助,但我宁愿使用本机数组并理解底层问题)。

2 个答案:

答案 0 :(得分:1)

所以对于clang来说,看起来有一些修复方法。你可以改变:

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

通过const引用获取foo

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

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

//constexpr SFoo(SFoo      &) =default;

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

另一方面,gcc抱怨复制构造函数说隐式定义不是constexpr( see it 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 [expr.const] 时,这对我来说并不明显。< / p>

据我在阅读第12.8p26节时所知,隐式定义的复制/移动分配应该是constexpr。所以gcc在这里似乎不正确。

答案 1 :(得分:0)

这不是由于阵列成员造成的。这是由于赋值运算符。如果您进行以下更改,则代码将适用于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) {}
};

Live Demo

如果使用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 = {};
};

Live Demo

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