初始化一个不可复制不可移动的元组类
Initialize a tuple of non-copyable and non-movable classes
考虑 class A 和唯一的自定义构造函数:
class A
{
public:
A(float) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
};
还有另一个class B,它包含A的元组(为简单起见,让它成为唯一的元组成员):
class B
{
public:
B() : ta(0.0f) {} // ta initialization OK
private:
std::tuple<A> ta;
};
现在我们可以声明 B 的一个对象并且它工作正常:
B b;
但是如果 A
的构造函数有多个参数怎么办呢?
class A
{
public:
A(float, int) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
};
class B
{
public:
// B() : ta(0.0f, 1) {} // Compilation errors
// B() : ta({0.0f, 1}) {} // Compilation errors
// B() : ta{0.0f, 1} {} // Compilation errors
// B() : ta(A(0.0f, 1)) {} // No constructor to copy or move A
private:
std::tuple<A> ta;
};
B b;
std::make_tuple
、std::forward_as_tuple
和其他类似的不能解决问题,因为 A
的默认、复制和移动构造函数被禁用。
std::tuple
几乎没有构造其成员的工具 in-situ。通常预期的初始化 tuple
的方法是通过其组件对象的 copy/move。
它确实允许将构造函数参数隐式转换为其各自的实参。但这只是参数和 tuple
成员之间的 1:1 关系。构造函数参数和 tuple
成员之间无法建立 many-to-one 关系。
您可以允许您的类型可以从tuple
本身隐式构造:
class A
{
public:
A(float, int) {}
A(const std::tuple<float, int> &tpl)
: A(std::get<0>(tpl), std::get<1>(tpl)) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
};
因此,您可以像这样构造一个 tuple
:
class B
{
public:
B() : ta(std::tuple<float, int>(0.0f, 1)) {}
private:
std::tuple<A> ta;
};
如果你想使用更深层次的元编程,你可能还能够制作一组构造函数,允许你的类型从 tuple
构造,其类型匹配任何可用的构造函数:
class A
{
public:
A(float, int) {}
template<typename ...Args>
A(const std::tuple<Args...> &args) : A(args, std::index_sequence_for<Args...>{}) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
template<typename ...Args, std::size_t ...Is>
A(const std::tuple<Args...> &args, std::index_sequence<Is...>)
: A(std::get<Is>(args)...) {}
};
考虑 class A 和唯一的自定义构造函数:
class A
{
public:
A(float) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
};
还有另一个class B,它包含A的元组(为简单起见,让它成为唯一的元组成员):
class B
{
public:
B() : ta(0.0f) {} // ta initialization OK
private:
std::tuple<A> ta;
};
现在我们可以声明 B 的一个对象并且它工作正常:
B b;
但是如果 A
的构造函数有多个参数怎么办呢?
class A
{
public:
A(float, int) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
};
class B
{
public:
// B() : ta(0.0f, 1) {} // Compilation errors
// B() : ta({0.0f, 1}) {} // Compilation errors
// B() : ta{0.0f, 1} {} // Compilation errors
// B() : ta(A(0.0f, 1)) {} // No constructor to copy or move A
private:
std::tuple<A> ta;
};
B b;
std::make_tuple
、std::forward_as_tuple
和其他类似的不能解决问题,因为 A
的默认、复制和移动构造函数被禁用。
std::tuple
几乎没有构造其成员的工具 in-situ。通常预期的初始化 tuple
的方法是通过其组件对象的 copy/move。
它确实允许将构造函数参数隐式转换为其各自的实参。但这只是参数和 tuple
成员之间的 1:1 关系。构造函数参数和 tuple
成员之间无法建立 many-to-one 关系。
您可以允许您的类型可以从tuple
本身隐式构造:
class A
{
public:
A(float, int) {}
A(const std::tuple<float, int> &tpl)
: A(std::get<0>(tpl), std::get<1>(tpl)) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
};
因此,您可以像这样构造一个 tuple
:
class B
{
public:
B() : ta(std::tuple<float, int>(0.0f, 1)) {}
private:
std::tuple<A> ta;
};
如果你想使用更深层次的元编程,你可能还能够制作一组构造函数,允许你的类型从 tuple
构造,其类型匹配任何可用的构造函数:
class A
{
public:
A(float, int) {}
template<typename ...Args>
A(const std::tuple<Args...> &args) : A(args, std::index_sequence_for<Args...>{}) {}
private:
A() = delete;
A(const A&) = delete;
A(A&&) = delete;
template<typename ...Args, std::size_t ...Is>
A(const std::tuple<Args...> &args, std::index_sequence<Is...>)
: A(std::get<Is>(args)...) {}
};