不使用聚合初始化直接初始化不可复制、不可移动的成员
Directly initializing non-copyable, non-movable members without using aggregate initialization
背景
假设我正在尝试使用平面数组实现固定大小的多维数组:
template <class T, std::size_t... Dims>
struct multi_array {
static constexpr std::size_t size() noexcept
{
return (Dims * ... * std::size_t{1});
}
std::array<T, size()> _elems;
};
_elems
成员 public 启用不可复制、不可移动类型的聚合初始化:(假设 non_movable
有一个明确的 (int)
构造函数)
multi_array<non_movable, 2, 3> arr {
non_movable(0), non_movable(1), non_movable(2),
non_movable(3), non_movable(4), non_movable(5)
};
这要归功于 C++17 保证的复制省略——_elems
的相应元素直接从未实现的纯右值初始化,不需要移动构造函数。
问题
现在的问题是:在上面的声明中,多维数组像一维数组一样被初始化。我将其称为 "flat initialization",与 "nested initialization":
形成对比
multi_array<non_movable, 2, 3> arr {
{ non_movable(0), non_movable(1), non_movable(2) },
{ non_movable(3), non_movable(4), non_movable(5) }
}; // error: too many initializers for 'multi_array<non_movable, 3, 2>'
我们如何在不将用于实现multi_array
的底层容器从一维数组更改为多维数组的情况下启用嵌套初始化?
我想这需要自定义构造函数,但我不知道如何通过构造函数传递未实现的纯右值 "transparently"。我能想到的就是用它们构造一个参数,然后从参数中移动,这对不可移动的类型不起作用。
最小的可重现示例
#include <array>
#include <cstddef>
struct non_movable {
explicit non_movable(int) {}
non_movable(const non_movable&) = delete;
non_movable(non_movable&&) = delete;
non_movable& operator=(const non_movable&) = delete;
non_movable& operator=(non_movable&&) = delete;
~non_movable() = default;
};
template <class T, std::size_t... Dims>
struct multi_array {
static constexpr std::size_t size() noexcept
{
return (Dims * ... * std::size_t{1});
}
std::array<T, size()> _elems;
};
int main()
{
multi_array<non_movable, 3, 2> arr {
non_movable(0), non_movable(1), non_movable(2),
non_movable(3), non_movable(4), non_movable(5)
};
// multi_array<non_movable, 3, 2> arr {
// { non_movable(0), non_movable(1), non_movable(2) },
// { non_movable(3), non_movable(4), non_movable(5) }
// };
(void)arr;
}
I have no idea how to pass unmaterialized prvalues "transparently" through constructors.
你想要的一般是不可能的。一旦将纯右值作为参数传递给函数,它将初始化一个对象参数,或者它会显示一个绑定到引用参数的临时值。无论哪种方式,它都不再是纯右值。
你能为你的特定用例做的最好的事情就是将它保持为一个聚合,或者只接受你必须将它初始化为一个 "flat" 多维数组,或者通过存储使其成为一个真正的多维数组数组中的数组。两种选择都有权衡。
背景
假设我正在尝试使用平面数组实现固定大小的多维数组:
template <class T, std::size_t... Dims>
struct multi_array {
static constexpr std::size_t size() noexcept
{
return (Dims * ... * std::size_t{1});
}
std::array<T, size()> _elems;
};
_elems
成员 public 启用不可复制、不可移动类型的聚合初始化:(假设 non_movable
有一个明确的 (int)
构造函数)
multi_array<non_movable, 2, 3> arr {
non_movable(0), non_movable(1), non_movable(2),
non_movable(3), non_movable(4), non_movable(5)
};
这要归功于 C++17 保证的复制省略——_elems
的相应元素直接从未实现的纯右值初始化,不需要移动构造函数。
问题
现在的问题是:在上面的声明中,多维数组像一维数组一样被初始化。我将其称为 "flat initialization",与 "nested initialization":
形成对比multi_array<non_movable, 2, 3> arr {
{ non_movable(0), non_movable(1), non_movable(2) },
{ non_movable(3), non_movable(4), non_movable(5) }
}; // error: too many initializers for 'multi_array<non_movable, 3, 2>'
我们如何在不将用于实现multi_array
的底层容器从一维数组更改为多维数组的情况下启用嵌套初始化?
我想这需要自定义构造函数,但我不知道如何通过构造函数传递未实现的纯右值 "transparently"。我能想到的就是用它们构造一个参数,然后从参数中移动,这对不可移动的类型不起作用。
最小的可重现示例
#include <array>
#include <cstddef>
struct non_movable {
explicit non_movable(int) {}
non_movable(const non_movable&) = delete;
non_movable(non_movable&&) = delete;
non_movable& operator=(const non_movable&) = delete;
non_movable& operator=(non_movable&&) = delete;
~non_movable() = default;
};
template <class T, std::size_t... Dims>
struct multi_array {
static constexpr std::size_t size() noexcept
{
return (Dims * ... * std::size_t{1});
}
std::array<T, size()> _elems;
};
int main()
{
multi_array<non_movable, 3, 2> arr {
non_movable(0), non_movable(1), non_movable(2),
non_movable(3), non_movable(4), non_movable(5)
};
// multi_array<non_movable, 3, 2> arr {
// { non_movable(0), non_movable(1), non_movable(2) },
// { non_movable(3), non_movable(4), non_movable(5) }
// };
(void)arr;
}
I have no idea how to pass unmaterialized prvalues "transparently" through constructors.
你想要的一般是不可能的。一旦将纯右值作为参数传递给函数,它将初始化一个对象参数,或者它会显示一个绑定到引用参数的临时值。无论哪种方式,它都不再是纯右值。
你能为你的特定用例做的最好的事情就是将它保持为一个聚合,或者只接受你必须将它初始化为一个 "flat" 多维数组,或者通过存储使其成为一个真正的多维数组数组中的数组。两种选择都有权衡。