c++ std::initializer_list的编译时串联

yxyvkwin  于 2022-11-19  发布在  其他
关注(0)|答案(2)|浏览(132)

我想写一些代码如下:

using int_list_t = std::initializer_list<int>;

struct ThreeDimensionalBox {
  static constexpr int_list_t kDims = {1, 2, 3};
};

struct FourDimensionalBox {
  static constexpr int_list_t kDims = {4, 5, 6, 7};
};

template<typename Box1, typename Box2>
struct CombinedBox {
  static constexpr int_list_t kDims = Box1::kDims + Box2::kDims;  // error
};

using SevenDimensionalBox = CombinedBox<ThreeDimensionalBox, FourDimensionalBox>;

是否有某种方法可以修复CombinedBox的实现,以便SevenDimensionalBox::kDims有效地绑定到{1, 2, 3, 4, 5, 6, 7}
我知道我可以用一个自定义的模板类替换std::initializer_list<int>,这个模板类带有一个variadic int模板参数列表,通过标准的元编程递归技术可以有效地实现连接。我只是想知道是否存在只使用std::initializer_list的解决方案。

bz4sfanl

bz4sfanl1#

std::initializer_list只能初始化为空、使用大括号括起的元素列表或通过复制。
然而,即使使用复制构造,std::initializer_list引用的实际数组的生存期也是由原始std::initializer_list对象的生存期决定的,原始std::initializer_list对象是由一个用大括号括起来的元素列表初始化的。换句话说,复制std::initializer_list并不复制数组或延长其生存期。
因此,不可能将std::initializer_list连接起来。它们不应被用作容器。std::initializer_list通常只应用作函数参数,作为一种轻量级的方式,将未指定大小的元素列表(用大括号括起来)传递给函数,以便函数进一步处理这些元素。
您可能需要std::array来代替。类似于以下代码(需要C17,但不需要C20):

struct ThreeDimensionalBox {
  static constexpr auto kDims = std::array{1, 2, 3};
};

struct FourDimensionalBox {
  static constexpr auto kDims = std::array{4, 5, 6, 7};
};

template<typename T, std::size_t N, std::size_t M>
constexpr auto concat_arrays(const std::array<T, N>& a, const std::array<T, M>& b) {
    // assumes `T` is default-constructible
    std::array<T, N+M> r;
    std::copy(std::begin(a), std::end(a), std::begin(r));
    std::copy(std::begin(b), std::end(b), std::begin(r)+N);
    return r;
}

template<typename Box1, typename Box2>
struct CombinedBox {
  static constexpr auto kDims = concat_arrays(Box1::kDims, Box2::kDims);
};

using SevenDimensionalBox = CombinedBox<ThreeDimensionalBox, FourDimensionalBox>;
ej83mcc0

ej83mcc02#

std::initializer_list只有一个用途:作为一个初始化对象的工具,在一个花括号初始化列表({}中的东西)被用于初始化该对象的范围内。关于这个类型的一切都是为此目的而构建的,并且它有一堆防火墙来防止你用它做其他任何事情。
其中最重要的是initializer_list实际上并不存储任何东西;它 * 引用 * 存储在其他位置的值数组(也就是创建它的{}的引用)。由于它不存储数组,并且它们只能从{}语法结构构建,因此您不能给予它们一个数组来引用。它们只能通过复制从现有的initializer_list对象中获取数组。并且只能从{}或现有的initializer_list中获得一个,而现有的initializer_list只能从......嗯,您明白了。
如果你需要进行编译时数组操作,std::array是一个更好的工具。如果你想连接两个花括号初始化列表,并使用结果初始化某个对象...你不能这样做。
不要尝试将initializer_list用作快速和脏的数组类型。只在初始化对象时使用它。

相关问题