将初始化列表/聚合初始化转发给 std::array 成员
Forwarding intializer list / aggregate inialization to std::array member
我有一个 class 封装了一个 stl 兼容的容器类型,它是唯一的 class 成员,并提供了很多可以应用于该向量的向量数学函数。
这个class有多种构造函数,其中一种是采用初始化列表的构造函数:
template <class Type, class VectorType = std::vector<Type>>
class MathVector
{
public:
using initializer_list = std::initializer_list<Type>;
MathVector (initializer_list il) : vector(il) {}
// many more constructors and member functions here
private:
VectorType vector:
}
虽然像 MathVector<int> foo = { 1, 2, 3 }
这样的东西运行良好,但 MathVector<int, std::array<int, 3>> bar = { 1, 2, 3 }
无法在 clang 上编译并出现像
这样的错误
(86, 55) No matching constructor for initialization of 'std::__1::array<int, 3>'
std::vector<int> foo = { 1, 2, 3 }
和 std::array<int, 3> bar = { 1, 2, 3 }
有效,所以我猜想尽管语法相同,但在这种情况下 std::array
并不是真正通过初始化列表构造的。当查看 std 库源时,我没有找到任何基于 std::array
构造函数的初始化列表,这种猜测变得更加强烈。此外,cppreference tells me that it can be initialized with aggregate-initialization——它甚至看起来都不是任何一种常见的构造函数。那么有没有一种方法可以为我的 class 创建一个构造函数,它可以将具有所需语法的初始化正确地转发给 std::array
成员?
使用标签调度:
template <typename T>
struct type_identity { using type = T; };
template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
MathVector(std::initializer_list<Type> il)
: MathVector(il, type_identity<VectorType>{}) {}
private:
template <typename T>
MathVector(std::initializer_list<Type> il, type_identity<T>)
: vector(il) {}
template <typename T, std::size_t N>
MathVector(std::initializer_list<Type> il, type_identity<std::array<T, N>>)
: MathVector(il, std::make_index_sequence<N>{}) {}
template <std::size_t... Is>
MathVector(std::initializer_list<Type> il, std::index_sequence<Is...>)
: vector{ *(il.begin() + Is)... } {}
VectorType vector;
};
另一种解决方案是使用可变模板构造函数。
template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
template <typename... Ts>
MathVector(Ts&&... ts)
: vector{ std::forward<Ts>(ts)... } {}
MathVector(MathVector& rhs)
: MathVector(const_cast<const MathVector&>(rhs)) {}
MathVector(const MathVector& rhs)
: vector(rhs.vector) {}
private:
VectorType vector;
};
或 c++20 更短:
template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
MathVector(std::convertible_to<Type> auto&&... ts)
: vector{ std::forward<decltype(ts)>(ts)... } {}
private:
VectorType vector;
};
我有一个 class 封装了一个 stl 兼容的容器类型,它是唯一的 class 成员,并提供了很多可以应用于该向量的向量数学函数。
这个class有多种构造函数,其中一种是采用初始化列表的构造函数:
template <class Type, class VectorType = std::vector<Type>>
class MathVector
{
public:
using initializer_list = std::initializer_list<Type>;
MathVector (initializer_list il) : vector(il) {}
// many more constructors and member functions here
private:
VectorType vector:
}
虽然像 MathVector<int> foo = { 1, 2, 3 }
这样的东西运行良好,但 MathVector<int, std::array<int, 3>> bar = { 1, 2, 3 }
无法在 clang 上编译并出现像
(86, 55) No matching constructor for initialization of 'std::__1::array<int, 3>'
std::vector<int> foo = { 1, 2, 3 }
和 std::array<int, 3> bar = { 1, 2, 3 }
有效,所以我猜想尽管语法相同,但在这种情况下 std::array
并不是真正通过初始化列表构造的。当查看 std 库源时,我没有找到任何基于 std::array
构造函数的初始化列表,这种猜测变得更加强烈。此外,cppreference tells me that it can be initialized with aggregate-initialization——它甚至看起来都不是任何一种常见的构造函数。那么有没有一种方法可以为我的 class 创建一个构造函数,它可以将具有所需语法的初始化正确地转发给 std::array
成员?
使用标签调度:
template <typename T>
struct type_identity { using type = T; };
template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
MathVector(std::initializer_list<Type> il)
: MathVector(il, type_identity<VectorType>{}) {}
private:
template <typename T>
MathVector(std::initializer_list<Type> il, type_identity<T>)
: vector(il) {}
template <typename T, std::size_t N>
MathVector(std::initializer_list<Type> il, type_identity<std::array<T, N>>)
: MathVector(il, std::make_index_sequence<N>{}) {}
template <std::size_t... Is>
MathVector(std::initializer_list<Type> il, std::index_sequence<Is...>)
: vector{ *(il.begin() + Is)... } {}
VectorType vector;
};
另一种解决方案是使用可变模板构造函数。
template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
template <typename... Ts>
MathVector(Ts&&... ts)
: vector{ std::forward<Ts>(ts)... } {}
MathVector(MathVector& rhs)
: MathVector(const_cast<const MathVector&>(rhs)) {}
MathVector(const MathVector& rhs)
: vector(rhs.vector) {}
private:
VectorType vector;
};
或 c++20 更短:
template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
MathVector(std::convertible_to<Type> auto&&... ts)
: vector{ std::forward<decltype(ts)>(ts)... } {}
private:
VectorType vector;
};