提问人:tommsch 提问时间:2/7/2022 最后编辑:Ted Lyngmotommsch 更新时间:2/8/2022 访问量:304
像 Initialization 一样聚合,无需多余调用构造函数
Aggregate like Initialization without superfluous calls of constructors
问:
我正在尝试主要编写 .但是,我对聚合初始化的规则(以及其他一些小细节)不是很满意,因此不想使用它。std::array
我不喜欢聚合启动的事情:
- 列表中的元素少于必要的元素,不会产生错误,这对于聚合的聚合尤其奇怪,例如
struct A { int x,y; }; std::array< A, 2 > arr{1,2,3}; // produces A[0].x = 1, A[0].y = 2, A[1].x = 1, A[1].y = 0
- 用户仍可调用私有/已删除的 CTOR
class B { B() = delete; }; B{}; // no compile time error
到目前为止,我所拥有的如下:
#include <cstddef>
#include <type_traits>
#include <utility>
template< typename T, typename ... Ts >
inline constexpr bool areT_v = std::conjunction_v< std::is_same<T, Ts> ... >;
namespace ttt {
template< typename T, int N >
struct array
{
template< typename ... L >
array( L && ... lst ) : content{ std::forward< L >( lst ) ... } {
static_assert( sizeof...(L) == N || sizeof...(L)==0, "" );
static_assert( areT_v<T,L...>, "" );
}
array() = default; // not sure whether I need this one
using ContentType = typename std::conditional< N>=1 , T, char >::type;
ContentType content[ N>=1 ? N : 1];
/* member functions */
};
}
实际上,除了以下事实之外,我对这种实现非常满意。使用时,我有更多的 ctor 调用,然后使用 .例如:ttt::array
std::array
#include <array>
#include <iostream>
struct CLASS {
int x, y;
CLASS() {
std::cout << "def-ctor" << std::endl;
}
CLASS(int x_,int y_) : x(x_), y(y_) {
std::cout << "xy-ctor" << std::endl;
}
CLASS( CLASS const & other ) : x(other.x), y(other.y) {
std::cout << "copy-ctor" << std::endl;
}
};
int main() {
std::array< CLASS, 1 > stda{CLASS{1,2}}; // calls xy-ctor
ttt::array< CLASS, 1 > ttta{CLASS{1,2}}; // calls xy-ctor AND copy-ctor
}
这似乎与聚合初始化的原因有关(因为它是聚合),但事实并非如此。
这让我想到了一个问题,编译器是否允许在这里做某事(省略一个 ctor 调用),我不被允许做?std::array
ttt::array
而且,有没有办法解决这个问题?
答:
1赞
Ted Lyngmo
2/8/2022
#1
您可以打包应该用于构造元素的参数,并延迟实际对象的创建,直到需要初始化。content
std::tuple
content
大纲:
template <class T, std::size_t N>
struct array {
template<class... Ts>
constexpr array(Ts&&... args) :
content{to_t(std::forward<Ts>(args),
std::make_index_sequence<std::tuple_size<Ts>::value>())...}
{
static_assert(sizeof...(Ts) == N, "wrong number of elements");
}
private:
// create one T from a tuple
template<class... Ts, std::size_t... Idx>
T to_t(std::tuple<Ts...>&& in, std::index_sequence<Idx...>) {
return T{std::forward<Ts>(std::get<Idx>(in))...};
}
T content[N];
};
要放入的示例类型:array
struct Foo {
Foo() : a(0.), b(0) { std::cout << "Foo::Foo()\n"; }
explicit Foo(double A, int B) : a(A), b(B) { std::cout << "Foo::Foo(double, int)\n"; }
Foo(const Foo& o) : a(o.a), b(o.b) { std::cout << "Foo(const Foo&)\n"; }
Foo(Foo&& o) : a(o.a), b(o.b) { std::cout << "Foo(Foo&&)\n"; }
~Foo() { std::cout << "Foo::~Foo() {" << a << ", " << b << "}\n"; }
private:
double a;
int b;
};
然后,你可以在不复制/移动你的 :s 的情况下创建你的 (在 C++ 17 中保证,在 C++ 14 中复制/移动省略,所以不能保证但可能)通过将它们打包成 s 来创建。打字有点麻烦。希望其他人对如何解决这个问题有一个想法。array
T
tuple
int main() {
Foo f{3.3, 3};
array<Foo, 4> x(std::forward_as_tuple(1.1, 1),
std::forward_as_tuple(), // default constructor
std::forward_as_tuple(2.2, 2),
std::forward_as_tuple(std::move(f)) // move constructor
);
}
演示
您可以添加编译器选项,以查看在 C++14 模式下关闭该优化时它将移动元素。-fno-elide-constructors
评论
0赞
tommsch
2/8/2022
我是否正确理解这是类似于 ,但对于一次给出的所有参数?emplace_back
0赞
Ted Lyngmo
2/8/2022
@tommsch 是的,这就是我的目标。我认为它可以通过助手功能变得更好。我会更多地考虑这个问题,如果我想出任何东西,我会更新答案
评论
CLASS
ttt::array
content
CLASS
content
ttt::array
array