Почему квалификатор const в псевдониме этого типа опущен?

TL;DR

Учитывая следующий тип:

struct A
{
    std::vector<std::string> vec;

    using reference = std::iterator_traits<decltype(vec)::iterator>::reference;
    using const_reference = const reference;
};

Почему reference == const_reference? Почему квалификатор const опущен во втором псевдониме типа?

См. пример на godbold, который не должен компилироваться.

Подробности

У меня есть шаблонный класс, который принимает кучу итераторов (типов) в качестве аргументов шаблона. Из этих итераторов мне нужно вывести ссылочный и константный ссылочный тип, потому что у меня есть некоторые функции-члены, такие как:

struct A
{
    std::vector<std::string> vec;

    using reference = std::iterator_traits<decltype(vec)::iterator>::reference;
    using const_reference = const reference;

    const_reference foo() const
    {
        return vec[0];
    }
};

Отбрасывая квалификатор const, я фактически возвращаю ссылку в foo, что является недопустимым, поскольку это константная функция-член, поэтому компилятор выдает исключение.


person Timo    schedule 22.05.2018    source источник
comment
reference это string&. const reference на самом деле string& const   -  person Passer By    schedule 22.05.2018
comment
На самом деле компилятор выдает довольно явное предупреждение: квалификатор 'const' для ссылочного типа 'A::reference' (иначе...) не имеет никакого эффекта [-Wignored-qualifiers]   -  person Holt    schedule 22.05.2018
comment
@Peter Но iterator_traits не имеет типа const_reference.   -  person Timo    schedule 22.05.2018
comment
@Holt Да, я видел это предупреждение, но не знал, почему оно появилось.   -  person Timo    schedule 22.05.2018


Ответы (2)


Он удален. То, что мы называем «константной ссылкой», на самом деле является ссылкой на константу — const int&.

Имея int& и добавляя const, получится int& const. Но такой const отбрасывается.

Ваша проблема похожа на разницу между const int* и int* const. За исключением того, что для ссылок int& и int& const имеют один и тот же тип - const игнорируется.

person Bo Persson    schedule 22.05.2018
comment
Мне трудно поверить, что где-то нет обмана. - person Passer By; 22.05.2018
comment
Вероятно, есть. Я начал как комментарий, как и вы, но он стал слишком большим, чтобы поместиться. :-) - person Bo Persson; 22.05.2018
comment
Ну, вероятно, есть дубликат, если вы правильно сформулируете вопрос. Но если вы не знаете корень проблемы, то сложно правильно его сформулировать. - person Timo; 22.05.2018
comment
@Timo Очевидно, что его нелегко найти, но клянусь, я уже видел этот вопрос раньше. - person Passer By; 22.05.2018
comment
Будет ли using const_reference = const std::remove_reference_t<reference>&; подходящей альтернативой или есть какие-то подводные камни? - person Timo; 22.05.2018
comment
@Timo - Это, вероятно, сработает. Я бы использовал std::vector<std::string>::const_reference и избегал жонглирования цепными пилами. - person Bo Persson; 22.05.2018
comment
@BoPersson Да, я бы тоже использовал это, если бы мог. Но, как я писал в вопросе, я не знаю, какие итераторы я получаю (они также могут быть нативными указателями), поэтому я должен использовать iterator_traits. - person Timo; 22.05.2018
comment
Так что я могу думать об этом как о том, что ОП делает const (T&), но на самом деле они хотят (const T)&. Трюк с remove_reference сработает, потому что вы можете извлечь T из (T&), добавить const, а затем вернуть ссылку. - person jcai; 22.05.2018

Ваша проблема - западная константа. Западная константа — плохая константа, восточная константа — лучшая константа.

West const помещает const слева от токена, который вы хотите сделать const. East const ставит его справа.

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

using const_reference = reference const;

вы, вероятно, можете понять, почему const не сработало. После наивного расширения ссылки вы получаете string&const -- здесь вы пытаетесь применить const к &, а не string, и foo &const не то же самое, что foo const& -- foo&const это просто foo&, поскольку const не может применяться к самой ссылке, а только к упомянутый тип.

Конечно, скажете вы, но именно поэтому я хочу west const!

West const делает то же самое здесь. Это относится к &, а не к string, а затем отбрасывается. Просто он делает это более запутанным и трудным для интуитивного понимания образом.

Чтобы понять константу, преобразуйте константу в восточную константу. West const — это просто исключение из стандартного правила east const, где, если в типе слева нет токена, он применяется к токену справа. Токен справа — это весь тип string&, связанный с псевдонимом типа (псевдонимы типов не являются макросами). Если бы вместо этого вы набрали const string&, токен справа был бы string, и вы получили бы string const& в здравом, восточно-константном стиле.

Восточная константа - лучшая константа.

person Yakk - Adam Nevraumont    schedule 22.05.2018
comment
Я не знаю, почему я нахожу это забавным - person Passer By; 22.05.2018
comment
Мне нравится терминология west и east const :) - person Timo; 22.05.2018