Другими словами: может ли класс 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
?
Зачем отмечать одновременно C++20 и C++26? Какую версию вы используете?
Я не понимаю, как приведенный пример иллюстрирует вопрос, он не использует никакого шаблона. Вы имели в виду Foo<T>
против Foo<const T>
?
Это совсем не то, о чем вы спрашивали, но ваша функция возможна, если Foo
является шаблоном класса: godbolt.org/z/K7nExGcGq
@Артьер, очень хитрый :O
@TedLyngmo моя база кода совместима с C++20, но мне интересна проблема в принципе, поэтому я добавил также самую последнюю версию.
@AdamRyczkowski C++23 еще даже не выпущен, а работа над C++26 только началась. Чтобы сузить круг задач, обычно лучше придерживаться той версии, с которой вы на самом деле компилируете.
Могу ли я специализировать шаблон класса на постоянстве типа?
Нет. 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
@AbhishekShivakumar В некотором роде зависит от интерпретации «свойств типа». Дело в том, что, за исключением того, что T
является отличным от const T
типом, оба типа имеют общие свойства. Единственное отличие состоит в том, что объект последнего типа имеет ограничения на модификацию и что выражение типа const
ведет себя по-разному при разрешении перегрузки и в том, что с ним можно использовать встроенные выражения.
«поскольку нет никакой гарантии, что расположение памяти для
T
иconst T
одинаково» Полагаю, ваша ссылка этого не говорит? Там написано, чтоvector<T>
иvector<const T>
могут быть разными, а вы это не то, что говорите.