当从std::array私有继承时,不能从std::initializer_list构造。

问题描述 投票:1回答:1

我想做一个std::array的封装器来执行边界检查,就像下面的建议一样 本回答. 这是我的代码。

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<int, 3>'.

如果我省略这一行 using std::array<T, N>::array; 并公开继承它的代码,虽然这个选项是不可取的。

我在这里遗漏了什么?为什么我的类不能创建这样的实例?

先谢谢你

c++ inheritance stl containers private
1个回答
3
投票

std::array 结构没有实现一个以 initializer_list. 实际上它只有一个隐式定义的构造函数。std::array解释说,每 [array.cons]不过,它确实符合集合的条件,所以可以通过 {1,2,3} 例如:

所规定的对集合体的要求(从 [dcl.init.aggr]/1.4)

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

因此,你的班级将无法与 private 基类。

注意,即使你把基类做成 public,你最终违反了 [dcl.init.aggr]/1.1其中指出

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

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

此处 的工作实例。


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 是一个不能进行值初始化的类,那么这个 Array 构造函数不能被调用,即使 N 提供了初始化器,因为编译器不能执行"il 包含 N 元素 "的条件,因此必须假定 T{} 可以在运行时被调用。没有办法完美地模拟集合初始化。

© www.soinside.com 2019 - 2024. All rights reserved.