隐藏空基类以进行聚合初始化
Posted
技术标签:
【中文标题】隐藏空基类以进行聚合初始化【英文标题】:Hide empty base class for aggregate initialization 【发布时间】:2020-04-17 07:06:11 【问题描述】:考虑以下代码:
struct A
// No data members
//...
;
template<typename T, size_t N>
struct B : A
T data[N];
这是你必须初始化 B 的方式:B<int, 3> b = , 1, 2, 3 ;
我想避免基类不必要的空 。
Jarod42 here 提出了一个解决方案,但是,它不适用于元素默认初始化:B<int, 3> b = 1, 2, 3;
很好,但 B<int, 3> b = 1;
不是:b.data[1]
和 b.data[2]
没有默认初始化为 0,并发生编译器错误。
有什么方法(或者c++20会有)从构造中“隐藏”基类?
【问题讨论】:
为什么不添加构造函数template<class... Ts> B(Ts... args) : dataargs...
?
为什么是评论?它似乎正在工作,大声笑
这是一个显而易见的解决方案,我认为您有理由不使用它。 :)
这太容易了xD。如果你把它写成答案,我会接受它
【参考方案1】:
从 C++20 开始,您可以在 aggregate initialization 中使用 designated initializers。
B<int, 3> b = .data 1 ; // initialize b.data with 1,
// b.data[0] is 1, b.data[1] and b.data[2] would be 0
【讨论】:
这对我来说仍然太冗长,这是一个最小的例子。我的数组成员有一个奇怪的名称,用户应该忽略它【参考方案2】:仍然使用构造函数,您可能会执行以下操作:
template<typename T, size_t N>
struct B : A
public:
constexpr B() : data
template <typename ... Ts,
std::enable_if_t<(sizeof...(Ts) != 0 && sizeof...(Ts) < N)
|| !std::is_same_v<B, std::decay_t<T>>, int> = 0>
constexpr B(T&& arg, Ts&&... args) : datastd::forward<T>(arg), std::forward<Ts>(args)...
T data[N];
;
Demo
SFINAE 主要是为了避免创建伪拷贝构造函数B(B&)
。
你需要额外的私有标签来支持B<std::index_sequence<0, 1>, 42>
;-)
【讨论】:
为什么需要((void)Is, T())...
?如果你只是省略它怎么办?其余元素不会默认使用T()
进行值初始化吗?
@Evg:确实,简化了。害怕只默认初始化剩余元素而不是值初始化它们......【参考方案3】:
最简单的解决方案是添加可变参数构造函数:
struct A ;
template<typename T, std::size_t N>
struct B : A
template<class... Ts, typename = std::enable_if_t<
(std::is_convertible_v<Ts, T> && ...)>>
B(Ts&&... args) : datastd::forward<Ts>(args)...
T data[N];
;
void foo()
B<int, 3> b1 = 1, 2, 3;
B<int, 3> b2 = 1;
如果您在...
初始化器列表中提供的元素少于N
,则数组data
中的剩余元素将按T()
进行值初始化。
【讨论】:
我刚刚发现了为什么这与聚合初始化不同。如果您考虑B<Class, 5> b = Class();
Class
将首先构造然后移动,而通过使用聚合初始化 Class
将在适当位置构造,不涉及移动
@user7769147,说得好。您可以使用std::tuple
的参数并使用它们就地构造对象。但是语法会比较麻烦。
我随机找到了一个可以解决这个问题的解决方案,我将把它作为已接受的答案,以感谢您的参与:)。【参考方案4】:
我找到了另一个解决方案(我不知道如何)完美地解决了我们在 Evg 的回答下讨论的问题
struct A ;
template<typename T, size_t N>
struct B_data
T data[N];
;
template<typename T, size_t N>
struct B : B_data<T, N>, A
// ...
;
【讨论】:
有趣的解决方案。但是现在必须使用this->data
或using B_data::data;
才能访问B
内部的data
。以上是关于隐藏空基类以进行聚合初始化的主要内容,如果未能解决你的问题,请参考以下文章