我如何在矩阵 class 中获得此功能?
How would I get this functionality in my matrix class?
#include <iostream>
#include <array>
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R * C> m_data;
};
int main()
{
matrix<float, 2, 2> a = { 1,2,3,4 }; // COMPILER ERROR!
}
Clang 报告没有匹配的构造函数!
我试过写一个构造函数
matrix(std::array<T,R*C> a);
并尝试使用 &&
进行试验,因为我认为相关表达式的右侧是暂时的。这让我有些困惑。正如我们所期望的那样,它将被创建然后分配(!)到 a
的值。
像评论中提到的其他人一样,您的 matrix
class.
需要一个 std::initializer_list<T>
构造函数
#include <array> // std::array
#include <initializer_list> // std::initializer_list
#include <algorithm> // std::copy
#include <cassert>
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R * C> m_data;
public:
matrix(const std::initializer_list<T> list)
// ^^^^^^^^^^^^^^^^^^^^^^^ --> constructor which takes std::initializer_list
{
assert(R * C == list.size());
std::copy(list.begin(), list.end(), m_data.begin());
}
};
int main()
{
matrix<float, 2, 2> a = { 1,2,3,4 }; // now compiles
}
然而,这不是编译时的,并且有以下缺点:
- 它不检查传递的类型是否相同。
- 无法在编译时检查传递列表的大小,因为
std::initializer_list
is/can 不是编译时。
- 第三,它允许缩小转换。
为了禁止上述情况,一种解决方案是提供一个可变参数模板构造函数,它可以在编译时启用上述检查。
类似如下:(See live online)
#include <array> // std::array
#include <initializer_list> // std::initializer_list
#include <type_traits> // std::conjunction, std::is_same
#include <utility> // std::forward
// traits for checking the types (requires C++17)
template <typename T, typename ...Ts>
using are_same_types = std::conjunction<std::is_same<T, Ts>...>;
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R * C> m_data;
public:
template<typename... Ts>
constexpr matrix(Ts&&... elemets) noexcept
{
static_assert(are_same_types<Ts...>::value, "types are not same!");
static_assert(sizeof...(Ts) == R*C, "size of the array does not match!");
m_data = std::array<T, R * C>{std::forward<Ts>(elemets)...};
}
};
int main()
{
matrix<float, 2, 2> a{ 1.f,2.f,3.f,4.f }; // now compiles
// matrix<float, 2, 2> a1{ 1,2,3,4 }; // error: narrowing conversion!
// matrix<float, 2, 2> a1{ 1.f,2.f,3.f, 4 }; // error: types are not same!
// matrix<float, 2, 2> a1{ 1.f,2.f,3.f,4.f, 5.f }; // error: size of the array does not match!
}
它的一个缺点是它不会检查您传递给它的参数数量。
template<typename T, std::size_t R, std::size_t C>
class matrix
{
public:
matrix(const std::initializer_list<T> list) { /*...*/ }
};
int main()
{
matrix<float, 2, 2> a = { 1,2,3,4 }; // compiles
matrix<float, 2, 2> b = { 1,2,3,4,5 }; // also compiles
}
改为使用以下内容:
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R*C> m_data;
public:
matrix() = default;
template<typename... Us>
matrix(Us &&...args) : m_data{static_cast<T>(args)...}
{
}
};
int main()
{
matrix<float,2,2> a = {1,2,3}; // ok
matrix<float,2,2> b = {1,2,3,4}; // ok
matrix<float,2,2> c = {1,2,3,4,5}; // error
}
强制转换对于避免潜在的缩小转换是必要的。
#include <iostream>
#include <array>
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R * C> m_data;
};
int main()
{
matrix<float, 2, 2> a = { 1,2,3,4 }; // COMPILER ERROR!
}
Clang 报告没有匹配的构造函数!
我试过写一个构造函数
matrix(std::array<T,R*C> a);
并尝试使用 &&
进行试验,因为我认为相关表达式的右侧是暂时的。这让我有些困惑。正如我们所期望的那样,它将被创建然后分配(!)到 a
的值。
像评论中提到的其他人一样,您的 matrix
class.
std::initializer_list<T>
构造函数
#include <array> // std::array
#include <initializer_list> // std::initializer_list
#include <algorithm> // std::copy
#include <cassert>
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R * C> m_data;
public:
matrix(const std::initializer_list<T> list)
// ^^^^^^^^^^^^^^^^^^^^^^^ --> constructor which takes std::initializer_list
{
assert(R * C == list.size());
std::copy(list.begin(), list.end(), m_data.begin());
}
};
int main()
{
matrix<float, 2, 2> a = { 1,2,3,4 }; // now compiles
}
然而,这不是编译时的,并且有以下缺点:
- 它不检查传递的类型是否相同。
- 无法在编译时检查传递列表的大小,因为
std::initializer_list
is/can 不是编译时。 - 第三,它允许缩小转换。
为了禁止上述情况,一种解决方案是提供一个可变参数模板构造函数,它可以在编译时启用上述检查。
类似如下:(See live online)
#include <array> // std::array
#include <initializer_list> // std::initializer_list
#include <type_traits> // std::conjunction, std::is_same
#include <utility> // std::forward
// traits for checking the types (requires C++17)
template <typename T, typename ...Ts>
using are_same_types = std::conjunction<std::is_same<T, Ts>...>;
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R * C> m_data;
public:
template<typename... Ts>
constexpr matrix(Ts&&... elemets) noexcept
{
static_assert(are_same_types<Ts...>::value, "types are not same!");
static_assert(sizeof...(Ts) == R*C, "size of the array does not match!");
m_data = std::array<T, R * C>{std::forward<Ts>(elemets)...};
}
};
int main()
{
matrix<float, 2, 2> a{ 1.f,2.f,3.f,4.f }; // now compiles
// matrix<float, 2, 2> a1{ 1,2,3,4 }; // error: narrowing conversion!
// matrix<float, 2, 2> a1{ 1.f,2.f,3.f, 4 }; // error: types are not same!
// matrix<float, 2, 2> a1{ 1.f,2.f,3.f,4.f, 5.f }; // error: size of the array does not match!
}
它的一个缺点是它不会检查您传递给它的参数数量。
template<typename T, std::size_t R, std::size_t C>
class matrix
{
public:
matrix(const std::initializer_list<T> list) { /*...*/ }
};
int main()
{
matrix<float, 2, 2> a = { 1,2,3,4 }; // compiles
matrix<float, 2, 2> b = { 1,2,3,4,5 }; // also compiles
}
改为使用以下内容:
template<typename T, std::size_t R, std::size_t C>
class matrix
{
std::array<T, R*C> m_data;
public:
matrix() = default;
template<typename... Us>
matrix(Us &&...args) : m_data{static_cast<T>(args)...}
{
}
};
int main()
{
matrix<float,2,2> a = {1,2,3}; // ok
matrix<float,2,2> b = {1,2,3,4}; // ok
matrix<float,2,2> c = {1,2,3,4,5}; // error
}
强制转换对于避免潜在的缩小转换是必要的。