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

Другими словами: может ли класс Foo пройти этот тест во время компиляции?

void prove_the_point() {
    Foo foo1{};
    const Foo foo2{};
    static_assert(sizeof(foo1) != sizeof(foo2));
}

Почему?

Я унаследовал большой проект в области науки о данных. Чтобы облегчить рассуждения в коде, я начал обеспечивать соблюдение константной корректности. Есть объекты, указатели на объекты, которые мне чаще всего приходилось заменять наshared_ptr, векторы и неизбежно векторы умных указателей.

Теперь вы можете видеть, к чему это идет: обеспечить константную корректность вектораshared_ptr, возможно, невозможно, потому что const не распространяется без изменения семантики API (например, путем предоставления итераторов вместо векторов указателей).

В комментариях к Как я могу распространить const при возврате std::vector<int*> из метода const?, @Voo предположил, что reinterpret_cast нельзя использовать, поскольку нет гарантии, что расположение памяти для T и const T одинаково. Если это правда, то это означает, что распространение const никогда не будет работать, потому что типы Foo и const Foo в принципе могут быть совершенно не связаны.

Это заставило меня задуматься, можно ли это доказать. Я уже знаю, что могу специализировать методы на постоянстве типа, но могу ли я специализировать весь класс, чтобы у меня были разные члены данных для T и const T?

«поскольку нет никакой гарантии, что расположение памяти для T и const T одинаково» Полагаю, ваша ссылка этого не говорит? Там написано, что vector<T> и vector<const T> могут быть разными, а вы это не то, что говорите.

HolyBlackCat 10.08.2024 11:43

Зачем отмечать одновременно C++20 и C++26? Какую версию вы используете?

Ted Lyngmo 10.08.2024 12:11

Я не понимаю, как приведенный пример иллюстрирует вопрос, он не использует никакого шаблона. Вы имели в виду Foo<T> против Foo<const T>?

François Andrieux 10.08.2024 12:34
std::experimental::propagate_const может помочь.
Jarod42 10.08.2024 12:46

Это совсем не то, о чем вы спрашивали, но ваша функция возможна, если Foo является шаблоном класса: godbolt.org/z/K7nExGcGq

Artyer 10.08.2024 12:47

@Артьер, очень хитрый :O

Enlico 10.08.2024 13:01

@TedLyngmo моя база кода совместима с C++20, но мне интересна проблема в принципе, поэтому я добавил также самую последнюю версию.

Adam Ryczkowski 10.08.2024 13:09

@AdamRyczkowski C++23 еще даже не выпущен, а работа над C++26 только началась. Чтобы сузить круг задач, обычно лучше придерживаться той версии, с которой вы на самом деле компилируете.

Ted Lyngmo 10.08.2024 13:12
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

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

Нет. const не является частью специализации шаблона.

Может ли класс Foo пройти этот тест времени компиляции?

Нет. sizeof игнорирует const. const не влияет на свойства типа.

обеспечить константную корректность вектораshared_ptr, возможно, невозможно, потому что

В самом const есть свойство shared_ptr, которого можно добиться, передав std::vector<std::shared_ptr<T>> как std::span<const std::shared_ptr<T>>.

Также существует свойство const в T, которое должно быть независимым от const в shared_ptr. Распространение shared_ptr с помощью const по всему вектору немного сложнее, в зависимости от того, что именно вы хотите сделать. Возникает вопрос, почему вам нужно передавать std::vector<std::shared_ptr<T>> таким образом, чтобы доступ к элементам T был const. Возможно, передача std::vector<const T*>, созданного на основе исходного вектора, более уместна, если право собственности не перешло.

В комментариях к статье «Как я могу распространять const при возврате std::vector<int*> из метода const?» @Voo предположил, что reinterpret_cast нельзя использовать, поскольку нет гарантии, что расположение памяти для T и const T является такой же. Если это правда, то это означает, что распространение const никогда не будет работать, потому что типы Foo и const Foo в принципе могут быть совершенно не связаны.

То, что reinterpret_cast имеет неопределенное поведение, не имеет никакого отношения к тому, связаны ли T и const T или имеют одинаковую компоновку. reinterpret_castмежду несвязанными типами почти во всех случаях происходит UB, независимо от макета. Макет вообще не определяет, допустимо ли такое приведение. Два типа, являющиеся специализациями одного и того же шаблона, не считаются «родственными». Кроме того, даже если T и const T абсолютно одинаковы во всех отношениях, нет никакой гарантии, что std::vector<const T> и std::vector<T> имеют одинаковый макет и свойства.

Вы правы, говоря, что const не меняет структуру памяти T, что и измеряет sizeof. Но это не значит, что const не влияет на свойства T ; это все еще влияет на то, как можно использовать T

Abhishek Shivakumar 10.08.2024 12:31

@AbhishekShivakumar В некотором роде зависит от интерпретации «свойств типа». Дело в том, что, за исключением того, что T является отличным от const T типом, оба типа имеют общие свойства. Единственное отличие состоит в том, что объект последнего типа имеет ограничения на модификацию и что выражение типа const ведет себя по-разному при разрешении перегрузки и в том, что с ним можно использовать встроенные выражения.

user17732522 10.08.2024 12:57

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

Ограниченная функция шаблона никогда не вызывается
Оператор трехстороннего сравнения по умолчанию в рекурсивных структурах данных
Есть ли способ дождаться разблокировки мьютекса (или аналогичного объекта), не блокируя его после этого?
Почему функция с параметром std::initializer_list не найдена, несмотря на попытку учесть ее с помощью объявления «using»
Почему автоматический вариант не работает по сравнению с (эквивалентной?) шаблонной версией функции?
Как написать функцию C++20, принимающую диапазон как с T, так и с const T?
Почему диапазоны::views::remove_if | ranges::to_vector и ranges::actions::remove_if генерируют другой код? И что мне предпочесть и почему?
Нужно ли объявлять оператор < перед вызовом std::lexicographical_compare?
Как я могу использовать лямбда-выражение в качестве постоянного основного выражения в предложении require?
C++ Передача реализации шаблонного интерфейса в качестве параметра