Как заставить мой векторный класс поддерживать прямую инициализацию / присваивание
Мой вопрос, конечно, глуп, но в моем понимании C++11 и программирования шаблонов так много пробелов, что я не знаю, как к этому подойти.
Я катлю свою собственную очень простую библиотеку линейной алгебры:
typedef short index_t;
template<int M, int N, typename T = double>
class mat {
// may want some specialized constructors for mat<1,N,T> and mat<M,1,T>
public:
T& operator()(index_t i, index_t j) {
return buf[i + j*M];
}
T& operator[](index_t k) { // useful for special cases where matrix is vector
return buf[k];
}
// etc...
private:
std::array<T, M*N> buf;
}
typedef mat<2, 1, double> col2d;
typedef mat<3, 1, double> col3d;
typedef mat<1, 2, double> row2d;
typedef mat<1, 3, double> row3d;
typedef mat<2, 2, double> mat2d;
typedef mat<3, 3, double> mat3d;
Я просто хочу, чтобы он поддерживал способ назначения (или, по крайней мере, инициализации) векторов (то есть матриц с одноэлементным измерением) напрямую. Например, я хотел бы иметь возможность сделать v = col2d(v1,v2)
или по крайней мере col2d v = {v1,v2}
, У меня сложилось впечатление, что делая buf
общественность может позволить col2d v = {{v1, v2}}
но мне не нравится идея разоблачения buf
, Я не заинтересован в написании специального конструктора для каждого (1,N) и каждого (M,1). Я стараюсь сделать библиотеку максимально простой и удобочитаемой.
Любой совет?
2 ответа
mat(std::array<T, M*N>&& buffin):buf(std::move(buffin)){}
mat(std::array<T, M*N> const& buffin):buf(buffin){}
Это не разоблачает buf
, но дает вам col3d x = {{{ 1,2,3 }}};
синтаксис.
Это также позволяет мату 3d инициализироваться с плоским буфером. Я нахожу это весьма полезным сам.
Я мог бы испытать желание добавить
struct flat_t{constexpr flat_t(){};};
constexpr flat_t flat{};
Затем добавьте array
ctors to mat
с flat_t
, Это предотвращает случайное неявное преобразование, в то же время return {flat, {{1,2,3}}};
возвращение стиля
Альтернатива это
template<class...Ts,
std::enavle_if_t< (sizeof...(Ts)==N*M), int> =0
>
mat(flat_t, Ts&&...ts):
buf{{std::forward<Ts>(ts)....}}
{}
Что дает вам
col2d{flat, 1,2};
как действительный col2d
,
Добавление теста sfinae, что все Ts
может быть преобразован в T
это следующий шаг на этом пути.
Вот что я в итоге сделал:
template<int M, int N, typename T = double>
class mat {
public:
// constructor
template <typename... Args>
mat(Args... args) : buf({ args... }) {
static_assert(sizeof...(Args) == M*N || sizeof...(Args)==0, "Wrong number of arguments in constructor.");
}
// etc...
}
// the following aliases replace all those typedefs in the question
template<index_t N, typename T=double>
using row = mat<1, N, T>;
template<index_t M, typename T=double>
using col = mat<M, 1, T>;
template <typename T=double, typename... Args>
col<sizeof...(Args), T> c(Args... args) {
return col<sizeof...(Args), T>({ args... });
}
template <typename T=double, typename... Args>
row<sizeof...(Args), T> r(Args... args) {
return row<sizeof...(Args), T>({ args... });
}
который позволяет такие вещи, как
c(1.0, 2.0, 3.0); // returns a col<3>, which is a mat<3,1>
r(1.0, 2.0); // returns a row<2>, which is a mat<1,2>
r<float>(1.0f, 2.0f); // returns a row<2, float>
Если для функции шаблона в последней строке выше есть простой способ вывести параметр типа из типа аргументов функции, я бы хотел узнать об этом.
РЕДАКТИРОВАТЬ: (Новая версия, включающая предложения @Yakk.)
typedef short index_t;
template<int M, int N, typename T = double>
class mat {
public:
mat() {
}
template <typename... Args, std::enable_if_t<(sizeof...(Args)==M*N), int> = 0> // static assertion doesn't work for external detection of validity, which is needed I think to avoid signature conflict with default, copy, move constructors etc.
mat(Args&&... args) : buf({ std::forward<Args>(args)... }) {
//static_assert(sizeof...(Args) == M*N || sizeof...(Args) == 0, "Wrong number of arguments in constructor.");
}
public:
T& operator()(index_t i, index_t j) {
return buf[i + j*M];
}
T& operator[](index_t k) {
return buf[k];
}
// etc... various matrix operations
private:
std::array<T, M*N> buf;
};
// etc... various matrix operators
// aliases for row and col
template<index_t N, typename T=double>
using row = mat<1, N, T>;
template<index_t M, typename T=double>
using col = mat<M, 1, T>;
// short-hand "literals"
template <typename... Args>
col<sizeof...(Args), typename std::common_type<Args...>::type> c(Args&&... args) {
return col<sizeof...(Args), typename std::common_type<Args...>::type>({ std::forward<Args>(args)... });
}
template <typename... Args>
row<sizeof...(Args), typename std::common_type<Args...>::type> r(Args&&... args) {
return row<sizeof...(Args), typename std::common_type<Args...>::type>({ std::forward<Args>(args)... });
}