Если у меня есть, например, некоторая структура шаблона, и я хочу присвоить ей определенный пользователем размер для какого-либо члена, я могу сделать это, передав значение конструктору следующим образом:
template <typename T>
struct Foo {
int m_size;
Foo(int u_size)
:
m_size {u_size}
{
}
};
и я также могу сделать это, имея параметр шаблона, не являющийся типом (вроде того, как это делает std::array
), например:
template <typename T, int u_size>
struct Foo {
int m_size;
Foo()
:
m_size {u_size}
{
}
};
Мой вопрос в том, в чем разница между этими двумя методами и когда полезно использовать любой из них?
std::array
нужно, чтобы его size
было известно во время компиляции, иначе он не будет работать, поскольку он имеет что-то вроде T data[size];
в качестве члена данных. Так что, если он работает для вас с аргументом конструктора, это, вероятно, (то есть в большинстве ситуаций) путь.
@paleonix член не const
@ 463035818_is_not_a_number да, но иметь разные m_size
и u_size
во время выполнения кажется плохой идеей. Иметь члена const
— тоже очень плохая идея. И да, вероятно, можно найти какой-то странный вариант использования, если бы это было разумно, но это было бы чрезвычайно редко.
«да, но наличие различий между m_size и u_size во время выполнения кажется плохим», почему? У вас может быть вектор с начальным размером 2, а позже вы измените его размер до 42. Это не пример, где вы сделали бы 2
параметр шаблона, но вы не можете исключить, что есть тот, где это имело бы смысл
@ 463035818_is_not_a_number «сомнительно требовать, чтобы экземпляр, использующий 42 в качестве начального значения, и экземпляр, использующий 24 в качестве начального значения, были разных типов». Мы соглашаемся...
@ 463035818_is_not_a_number Моя точка зрения заключается в том, что в большинстве случаев, когда оба варианта являются вариантами (т. Е. Не нужно использовать параметр шаблона), использование одного сравнимо с преждевременной оптимизацией. Но это был всего лишь намек и, следовательно, комментарий, а не ответ, в котором следует более подробно остановиться на такого рода вещах.
@paleonix, честно говоря, я не вижу связи с преждевременной оптимизацией. Я предпочитаю принимать решения во время компиляции, когда это возможно. Я делаю это не для большей производительности, а для более чистого дизайна. Кстати, я нашел хороший пример. Foo<int,42>
— это небоскрёб, цоколь которого имеет ширину 42. В течение жизни ширина верхнего этажа (m_size
) может меняться, но ширина цоколя постоянна (u_size
). Даже в этом примере я вижу в области дизайна, а не оптимизации, выбор между А) небоскребы с разной шириной подвала относятся к разным типам или Б) одного и того же типа
Давайте продолжим обсуждение в чате.
Вы используете u_size
для определения инициализатора члена m_size
. Разница между двумя примерами сводится к тому, что тип Foo<T,i>
отличается от типа Foo<T,j>
. Помимо того, что они являются экземплярами одного и того же шаблона (что обычно мало что значит), они совершенно не связаны между собой. С другой стороны, в первом случае любой Foo<T>
относится к одному и тому же типу Foo<T>
, каким бы ни было начальное значение члена.
Если m_size
изменяется во время выполнения, сомнительно требовать, чтобы экземпляр, использующий 42
в качестве начального значения, и экземпляр, использующий 24
в качестве начального значения, были разных типов.
Возможно, m_size
не меняется во время выполнения, тогда имеет смысл иметь начальное значение как часть типа (как, например, с std::array
). Возможно, оно меняется во время выполнения, но, тем не менее, есть причины запекать начальное значение в типе. Нет «лучше» или «хуже», это зависит от того, что вы хотите/нужно.
Есть даже типы, которые имеют фиксированное значение или магическое значение, чтобы сказать, что значение является динамическим (например, std::span.
Для версии параметра шаблона, отличного от типа, вообще не имеет смысла иметь другую переменную-член. Можно было бы просто использовать
u_size
для всего внутри класса.