Использование параметра шаблона против аргумента конструктора

Если у меня есть, например, некоторая структура шаблона, и я хочу присвоить ей определенный пользователем размер для какого-либо члена, я могу сделать это, передав значение конструктору следующим образом:

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}
    {
    }
};

Мой вопрос в том, в чем разница между этими двумя методами и когда полезно использовать любой из них?

Для версии параметра шаблона, отличного от типа, вообще не имеет смысла иметь другую переменную-член. Можно было бы просто использовать u_size для всего внутри класса.

paleonix 30.01.2023 11:20
std::array нужно, чтобы его size было известно во время компиляции, иначе он не будет работать, поскольку он имеет что-то вроде T data[size]; в качестве члена данных. Так что, если он работает для вас с аргументом конструктора, это, вероятно, (то есть в большинстве ситуаций) путь.
paleonix 30.01.2023 11:22

@paleonix член не const

463035818_is_not_a_number 30.01.2023 11:25

@ 463035818_is_not_a_number да, но иметь разные m_size и u_size во время выполнения кажется плохой идеей. Иметь члена const — тоже очень плохая идея. И да, вероятно, можно найти какой-то странный вариант использования, если бы это было разумно, но это было бы чрезвычайно редко.

paleonix 30.01.2023 11:29

«да, но наличие различий между m_size и u_size во время выполнения кажется плохим», почему? У вас может быть вектор с начальным размером 2, а позже вы измените его размер до 42. Это не пример, где вы сделали бы 2 параметр шаблона, но вы не можете исключить, что есть тот, где это имело бы смысл

463035818_is_not_a_number 30.01.2023 11:30

@ 463035818_is_not_a_number «сомнительно требовать, чтобы экземпляр, использующий 42 в качестве начального значения, и экземпляр, использующий 24 в качестве начального значения, были разных типов». Мы соглашаемся...

paleonix 30.01.2023 11:32

@ 463035818_is_not_a_number Моя точка зрения заключается в том, что в большинстве случаев, когда оба варианта являются вариантами (т. Е. Не нужно использовать параметр шаблона), использование одного сравнимо с преждевременной оптимизацией. Но это был всего лишь намек и, следовательно, комментарий, а не ответ, в котором следует более подробно остановиться на такого рода вещах.

paleonix 30.01.2023 11:50

@paleonix, честно говоря, я не вижу связи с преждевременной оптимизацией. Я предпочитаю принимать решения во время компиляции, когда это возможно. Я делаю это не для большей производительности, а для более чистого дизайна. Кстати, я нашел хороший пример. Foo<int,42> — это небоскрёб, цоколь которого имеет ширину 42. В течение жизни ширина верхнего этажа (m_size) может меняться, но ширина цоколя постоянна (u_size). Даже в этом примере я вижу в области дизайна, а не оптимизации, выбор между А) небоскребы с разной шириной подвала относятся к разным типам или Б) одного и того же типа

463035818_is_not_a_number 30.01.2023 14:33

Давайте продолжим обсуждение в чате.

paleonix 30.01.2023 15:19
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы используете u_size для определения инициализатора члена m_size. Разница между двумя примерами сводится к тому, что тип Foo<T,i> отличается от типа Foo<T,j>. Помимо того, что они являются экземплярами одного и того же шаблона (что обычно мало что значит), они совершенно не связаны между собой. С другой стороны, в первом случае любой Foo<T> относится к одному и тому же типу Foo<T>, каким бы ни было начальное значение члена.

Если m_size изменяется во время выполнения, сомнительно требовать, чтобы экземпляр, использующий 42 в качестве начального значения, и экземпляр, использующий 24 в качестве начального значения, были разных типов.

Возможно, m_size не меняется во время выполнения, тогда имеет смысл иметь начальное значение как часть типа (как, например, с std::array). Возможно, оно меняется во время выполнения, но, тем не менее, есть причины запекать начальное значение в типе. Нет «лучше» или «хуже», это зависит от того, что вы хотите/нужно.

Есть даже типы, которые имеют фиксированное значение или магическое значение, чтобы сказать, что значение является динамическим (например, std::span.

Jarod42 30.01.2023 14:29

Другие вопросы по теме