【问题标题】:Can't construct from std::initializer_list when privately inheriting from std::array从 std::array 私有继承时无法从 std::initializer_list 构造
【发布时间】:2020-09-22 11:42:42
【问题描述】:

我正在尝试制作 std::array 的包装器,以按照this answer 的建议执行边界检查。这是我的代码:

template <typename T, size_t N>
class Array : private std::array<T, N> {
public:
    using std::array<T, N>::array;

    T operator[](size_t i) {
        return this->at(i);
    }

    T operator[](size_t i) const {
        return this->at(i);
    }
};

int main() {
    Array<int, 3> arr = {0,0,0};
}

当我尝试运行它时,我收到以下错误:error: no matching constructor for initialization of 'Array&lt;int, 3&gt;'

如果我省略 using std::array&lt;T, N&gt;::array; 行并公开继承它,则代码有效,但不建议使用此选项。

我在这里缺少什么?为什么我的班级不能创建这样的实例?

提前致谢!

【问题讨论】:

  • 与其继承std::array,不如考虑启用边界检查,如果你的标准库支持的话。错误信息似乎来自 Clang;如果将它与 libstdc++ 一起使用,则正确的标志是 -D_GLIBCXX_DEBUG
  • array 实际上并没有采用initializer_list 的构造函数。
  • 澄清一下:它改用aggregate initialization
  • @HolyBlackCat 这种方法对我来说似乎更优雅,因为我不依赖不同的标准库版本...... @ChrisMM 我理解并将编辑这个问题。不过,我仍然不明白为什么我的班级不能以这种方式继承 aggregate_initialization
  • @gustavo,请参阅下面我答案的第二部分。

标签: c++ inheritance stl containers private


【解决方案1】:

std::array 被设计为聚合。它没有用户提供的构造函数,因此可以使用聚合初始化对其进行初始化。因为您的Array 类有一个私有基类,它不是聚合,只能由构造函数初始化。

另一种看待它的方式是,由于Array 具有对用户隐藏的成员,因此语言允许用户使用聚合语法直接初始化这些元素是没有意义的初始化一个普通数组。相反,用户必须调用构造函数,其中Array 类的作者已经明确实现了必要的初始化逻辑来履行Array 类的契约。

一个简单的解决方案是公开std::array 基类。如果你不想这样做,你可以编写自己的initializer_list 构造函数,但它很棘手且不完善:

// delegate to a helper constructor
Array(std::initializer_list<T> il) : Array(il, std::make_index_sequence<N>{}) {}

private:
template <size_t... i>
Array(std::initializer_list<T> il, std::index_sequence<i...>)
  : std::array<T, N>{(i < il.size() ? il.begin()[i] : T{})...} {}

辅助构造函数使用初始化列表中的一个元素来初始化对应的std::array元素,如果存在的话;否则,它会从 T{} 初始化它。

这样做的主要问题是,如果T 是一个无法进行值初始化的类,那么即使提供了N 初始化器,也无法调用此Array 构造函数,因为编译器无法强制执行“@ 987654334@ 在编译时包含N 元素”条件,因此必须假定T{} 可以在运行时调用。没有办法完美地模拟聚合初始化。

【讨论】:

    【解决方案2】:

    std::array 结构没有实现采用initializer_list 的构造函数。它实际上只有一个隐式定义的构造函数。 std::array,根据[array.cons],确实满足聚合的条件,因此可以通过{1,2,3} 进行初始化。

    指定的聚合要求(来自[dcl.init.aggr]/1.4

    没有虚拟、私有或受保护的基类

    因此,您的类将不适用于 private 基类。

    请注意,即使您创建基类 public,您最终还是违反了 [dcl.init.aggr]/1.1,其中规定

    没有用户提供的、显式的或继承的构造函数

    所以你必须摆脱你的 using 声明。

    有关工作示例,请参阅 here

    【讨论】:

    • 感谢您的快速回答,我想我必须在这里采取不同的方法。
    猜你喜欢
    • 2016-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多