Специализируйте только конструктор в шаблоне, сохраняя лучшую производительность и аккуратный интерфейс - PullRequest
0 голосов
/ 23 января 2019

1.Можно ли просто специализировать конструктор в шаблонном классе?

У меня есть такой пример:

// squared matrix class
template <unsigned int size>
class Matrix {
public:
    // list of lists matrix constructor
    Matrix(std::initializer_list<std::initializer_list<float>> values);

    // actual static data
    float data[size][size];
};

Так что в принципе я могу создать квадратные матрицы любого размера с помощью вложенного инициализаторасписки, как в:

Matrix <3> Identity {
    {1.,0.,0.},
    {0.,1.,0.},
    {0.,0.,1.}
};

Теперь я хотел бы предоставить только для матриц 3x3 конструктор, который берет 3 трехмерных вектора (класс Vec3 в моем коде) и использует их какстолбцы матрицы для построения матрицы 3x3.

Можно ли просто специализировать конструктор?

Если я специализируюсь на всем классе, например:

template <>
class Matrix<3> {
public:
    // build matrix providing 3 Vec3 (your basis) which will be the columns
    Matrix(std::initializer_list<Vec3> basis_vectors);
};

Затем я получаю ошибки внутри определения ctor:

Matrix<3>::Matrix(std::initializer_list<Vec3> basis_vectors) {

    // only accept 3 vectors
    assert(basis_vectors.size() == 3);

    // column counter
    unsigned int j = 0;

    for (auto & col : basis_vectors) {
        // copy a column (col to data[:][j])
        data[0][j] = col[0];
        data[1][j] = col[1];
        data[2][j] = col[2];

        // increase j for column
        j++;
    }
}

, например, «идентификатор« данные »не определен».

Примечания : я бы хотел избежать наследованиякак я могу.Странные шаблонные шаблоны могут быть хорошими, но, возможно, их лучше избегать (в противном случае все становится еще менее читабельным, чем сейчас).Хотелось бы отметить, что мой фокус - производительность.Поэтому я ищу самое быстрое и, возможно, самое чистое решение (например, чистое для пользователя, которому приходится создавать экземпляры этих классов. Поэтому я использую списки инициализаторов в конструкторе).


Дополнительные вопросы:

2.Какой правильный синтаксис для специализации конструктора?

Я имею в виду: мне нужно предоставить template <blabla> или template<> или ничего перед именем метода?А как насчет метода конструктора?Это Matrix() или Matrix<3>()?

3.Есть ли способ, позволяющий моему конструктору использовать синтаксис списка фигурных скобок при создании экземпляра объекта (чтобы иметь более чистый и равномерный синтаксис) при одновременном применении фиксированного размера списка?

В моемВ этом случае я хотел бы иметь конструктор, который просто принимает 3 Vec3 (и знает, что во время компиляции, без assert ing).Но я бы предпочел избежать подписи Matrix(Vec3 a, Vec3 b, Vec3 c).Возможно ли это?

4.Какой более чистый и удобный для меня способ сделать то, что я пытаюсь сделать, то есть установить столбцы в двумерном массиве?

Если для этого требуется полностью изменить мои структуры данных, используя некоторые аккуратныеC ++ 11/17/20 / что бы то ни было std::something, используя что-то, что уже реализует нарезку nD-массивов, или реализуя это сам, я здесь готов сделать все это.

Мне просто нужно немногохороший совет по всем этим вопросам.Так легко начать писать что-то, а потом потеряться во всех этих деталях и в итоге ничего не написать, чувствуя, что существует слишком много направлений, куда можно пойти, и это может привести к неоптимальному, нечитаемому,код болезненного рефакторинга: (

Ответы [ 2 ]

0 голосов
/ 24 января 2019

Другим вариантом, хотя и не полностью эквивалентным SFINAE, является использование static_assert:

Matrix(std::initializer_list<Vec3>)
{
    static_assert(size == 3);
    ...
}
0 голосов
/ 24 января 2019

Вопрос 1.

Можно ли просто специализировать конструктор в шаблонном классе?

Полагаю, вы выглядите следующим образом

   template <unsigned int s = size,
             std::enable_if_t< s == 3u, bool> = true>
   Matrix (std::initializer_list<Vec3> values)
    { }

Ниже приведен полностью упрощенный пример компиляции

#include <type_traits>
#include <initializer_list>

struct Vec3
 { };

template <unsigned int size>
struct Matrix
 {
   // list of lists matrix constructor
   Matrix (std::initializer_list<std::initializer_list<float>>)
    { }

   template <unsigned int s = size,
             std::enable_if_t< s == 3u, bool> = true>
   Matrix (std::initializer_list<Vec3> values)
    { }
 };

int main ()
 {
   Matrix<1>  m1 {{1.0f}};
   Matrix<2>  m2 {{1.0f}};
   Matrix<3>  m3 {{1.0f}};
   Matrix<4>  m4 {{1.0f}};

   //Matrix<1>  m5 {{Vec3{}}};  // compilation error
   //Matrix<2>  m6 {{Vec3{}}};  // compilation error
   Matrix<3>  m7 {{Vec3{}}};
   //Matrix<4>  m8 {{Vec3{}}};  // compilation error
 }

Это не совсем "специализация конструктора";это «включение SFINAE» для конструктора.


Вопрос 3.

Есть ли способ, позволяющий моему конструктору использовать синтаксис списка фигурных скобок при создании экземпляра объекта (чтобы иметьболее чистый и единообразный синтаксис) при одновременном применении фиксированного размера списка?

Не с std::initializer_list.

Но вы можете получить нечто подобное со старым добрымМассив в стиле C.

   template <unsigned int s = size,
             std::enable_if_t< s == 3u, bool> = true>
   Matrix (Vec3 const (&values)[3u])
    { }

Но это решение принимает не только три Vec3, но также один или два;конструктор всегда получает массив из трех с Vec3{}, инициализированным конструктором по умолчанию (если он доступен), когда не объяснено

   Matrix<3>  m7a {{Vec3{}}};
   Matrix<3>  m7b {{Vec3{}, Vec3{}}};
   Matrix<3>  m7c {{Vec3{}, Vec3{}, Vec3{}}};
   //Matrix<3>  m7d {{Vec3{}, Vec3{}, Vec3{}, Vec3{}}}; // compilation error

- РЕДАКТИРОВАТЬ -

OP спрашивает

1) должна ли реализация этого конструктора находиться внутри определения класса, или она может / должна быть снаружи (и с каким синтаксисом?)

I 'm используется для определения его внутри класса, но если вы объявляете его следующим образом внутри класса

   template <unsigned int s = size,
             std::enable_if_t< s == 3u, bool> = true>
   Matrix (std::initializer_list<Vec3> values);

, определяя его вне тела класса, то следующее должно работать

template <>
template <>
Matrix<3u>::Matrix (std::initializer_list<Vec3> values)
 { }

2) Это позволило бы мне передать любое количество Vec3 в конструктор, в то время как я хочу, чтобы мне разрешалось передавать 3 и только 3 Vec3 в списке инициализации.Можно ли это предсказать?

Я не думаю, что это возможно применить, пропуская через std::initializer_list, потому что количество элементов в списке не является значением шаблона (какв std::array или в массиве в стиле C) или в том, что мы можем использовать время компиляции для включения SFINAE.

Вы можете использовать решение на основе массива в стиле Vec3[3] C (см. мой ответ навопрос 3) но, к сожалению, принимаем также один или два Vec3.

...