Почему нет перегрузки std::as_const для типов указателей

Я только что наткнулся на std::as_const и был удивлен выводом последней строки в следующем фрагменте:

#include <cstdio>
#include <utility>

struct S {
    void foo() { std::puts("foo: non const"); }
    void foo() const { std::puts("foo: const"); }
};

int main() {
    S s;
    s.foo(); // foo: non const
    std::as_const(s).foo(); // foo: const

    auto* s_ptr = &s;
    s_ptr->foo(); // foo: non const
    std::as_const(s_ptr)->foo(); // foo: non const (?)
}

Глядя на документацию, я понимаю, почему вызывается не-const перегрузка foo: std::as_const(s_ptr) возвращает S* const&, то есть ссылку на константу указатель на неконстанту S вместо S const*, то есть указатель на константу S, как я бы ожидали.

Итак, мой вопрос: почему стандарт не предоставляет перегрузку std::as_const для типов указателей? Например. что-то вроде:

template <class T>
constexpr std::add_const_t<T>* as_const(T* t) noexcept {
    return t;
}

Редактировать: одним из мотивов для std::as_const в статье P0007R1 является выбор перегрузки функции без необходимости прибегать к const_cast. P0007R1 предоставляет этот пример:

int processEmployees( std::vector< Employee > &employeeList );
bool processEmployees( const std::vector< Employee > &employeeList );

A larger project often needs to call functions, like processEmployees, and selecting among specific const or non-const overloads. [...]

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

std::as_const(this)->foo();

ни при выборе последней из следующих перегрузок:

int processEmployees( std::vector< Employee > *employeeList );
bool processEmployees( const std::vector< Employee > *employeeList );

Предлагаемая разница в поведении была бы катастрофой для шаблонных функций.

HolyBlackCat 10.05.2022 23:57
"как я и ожидал" - вот в чем проблема. Прикрепленное к что-нибудь еще, "вещь" является константой. То, что указатели могут позволить себе роскошь прикреплять константность в двух местах (сам указатель и объект, на который он указывает), не меняет эту идиому std::as_const, и это не должен.
WhozCraig 10.05.2022 23:59

Что вы предлагаете int*** p; std::as_const(p) делать? Здесь есть четыре разных места, где можно представить const.

Igor Tandetnik 11.05.2022 00:06

Я спрашивая с вопросом. Я ничего не предложение.

paolo 11.05.2022 00:09

Что ж, вы ожидали, что as_const<int*> вернётся int const*, а не int* const. Чего бы вы ожидали от as_const<int***>?

Igor Tandetnik 11.05.2022 00:20

@IgorTandetnik Я ожидал, что std::as_const позволит мне выбрать void f(T const*) вместо void f(T*) так же, как он позволяет мне выбрать void f(T const&) вместо void f(T&). Итак, я ожидал, что std::as_const<int***> вернет мне int** const*.

paolo 11.05.2022 09:43
std::as_const(*this).foo();
n. 1.8e9-where's-my-share m. 11.05.2022 10:07

А как насчет unique_ptr и shared_ptr? А как насчет итераторов (всех, включая еще не написанные)? Вы ожидаете, что там произойдет что-то особенное?

n. 1.8e9-where's-my-share m. 11.05.2022 10:10

Что не так с std::as_const(*s_ptr).foo();?

Red.Wave 11.05.2022 11:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
9
101
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Цель std::as_const состоит в том, чтобы иметь возможность ссылаться на не-const lvalue как на const lvalue, чтобы его нельзя было изменить в контексте, в котором оно используется. Другими словами, std::as_const(x) должно быть сокращением для написания

const auto& y = x;

а затем с помощью y.

Он уже делает это хорошо, поэтому нет необходимости в специальном поведении для указателей.

А вот простой пример, когда предлагаемая дополнительная перегрузка будет иметь серьезные негативные последствия:

std::vector<int> vec = /*...*/;
for(auto it = std::begin(vec); it != std::end(vec); it++)
    func(std::as_const(it));

Цель здесь состоит в том, чтобы убедиться, что функция func не может изменить it, поскольку ответственность за итерацию по вектору лежит на цикле for. Если func просто принимает итератор по значению или ссылке const, то std::as_const не является строго обязательным, но в любом случае имеет смысл в качестве меры безопасности или потому, что существует несколько перегрузок func, некоторые из которых изменяют свой аргумент.

auto вот какой-то тип итератора. Это мог будет указателем. Или это может быть тип класса. С предложенной вами перегрузкой as_const это сломается в зависимости от того, как реализован итератор std::vector.

std::as_const(it) должен сказать, что it не может быть изменен при таком использовании. Он не должен ничего говорить о том, можно ли модифицировать объект, на который ссылается it. Это не его цель. Конечно, было бы целесообразно добавить функцию, которая делает объект упоминается немодифицируемым. Но у него должно быть другое имя, и вы, вероятно, захотите реализовать его для произвольных итераторов, а не конкретно для указателей. По сути, адаптер итератора к const-итератору.

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