Подтвердить что ты не робот

Почему допускается неявное преобразование из константы в константу?

Почему С++ позволяет компилировать следующий код?

std::unordered_map<std::string, int> m;
// ...
for (const std::pair<std::string, int>& p: m)
{
    // ...
}

Согласно Эффективный современный С++ Скотта Мейерса (стр. 40-41):

[...] ключевая часть std::unordered_map равна const, поэтому тип std::pair в хеш-таблице (что и есть std::unordered_map) isnt std::pair<std::string, int>, it std::pair <const std::string, int>. Но это не тип, объявленный для переменной p в цикле выше. В результате компиляторы будут стремиться найти способ преобразования объектов std::pair<const std::string, int> (т.е. В хэш-таблице) в объекты std::pair<std::string, int> (объявленный тип для p). Theyll удалось создать временный объект типа, который p хочет связать, копируя каждый объект в m, а затем привязывая ссылку p к этому временному объекту. В конце каждой итерации цикла временный объект будет уничтожен. Если вы написали этот цикл, вы, вероятно, будете удивлены этим поведением, потому что вы почти наверняка намерены просто привязать ссылку p к каждому элементу в m.

В чем преимущество разрешения этого неявного преобразования? Есть ли какой-то общий вариант использования, когда разработчик ожидает/предпочитает это неявное преобразование (а не получение ошибки компилятора)?

4b9b3361

Ответ 1

Соответствующий стандартам компилятор будет "видеть" цикл for следующим образом:

auto&& __range = m; 
for (auto __begin = std::begin(m), __end = std::end(m); __begin != __end; ++__begin) { 
    const std::pair<std::string, int>& p = *__begin;
    //loop_statement 
}

Что в основном сводит ваш вопрос к тому, почему разрешен следующий код:

std::pair<std::string, int> p = std::pair<const std::string, int>{};

Обратите внимание, что я опустил const& часть p, потому что это не актуально. Преобразование одно и то же, единственное различие заключается в том, что временная привязка к ссылке вместо копирования.

Если вам интересно, почему фрагмент OP не работает с неконстантной ссылкой, причиной является преобразование. Результатом преобразования является временный объект и потому, что любое изменение во временном случае будет бесполезным (его срок службы не расширяется и поэтому он сразу же уничтожается), поэтому язык запрещает его.

Это разрешено, потому что std::pair имеет конструктор, который разрешает это преобразование.

template< class U1, class U2 >
pair( const pair<U1, U2>& p );

В вашем случае U1 выводится как const std::string и U2 как int. На самом деле не имеет значения, какие атрибуты cv U1 и U2 имеют, поскольку элементы p копируются.

Преимущества одинаковы с тем, почему это разрешено:

const int zero{};
int zero2 = zero;

Например, рассмотрим следующий нереалистичный пример:

struct {
    std::pair<int, int> pos;
} player;

std::pair<const int, const int> treasure{1, 2}; // position of treasure
player.pos = treasure; // ok

Теперь, если, как вы говорите, это обращение по какой-то причине было запрещено. Что программист должен был сделать?

player.pos.first = treasure.first;
player.pos.second = treasure.second;

Если это также будет запрещено, то и случай с нулями выше также не будет разрешен, что на самом деле не имеет смысла, потому что вы копируете zero, поэтому не имеет значения, можете ли вы его изменить или нет, потому что это совершенно другая операция.

Если это разрешено, то почему бы player.pos = treasure; быть запрещенным, потому что единственное, что он делает, это копирование? Как и выше, не имеет значения, можете ли вы изменить элементы treasure, потому что вы только копируете их.

Также вы должны использовать auto&& или const auto& для диапазонов циклов (и, возможно, даже в целом?), потому что они могут избежать копирования, если вы не будете осторожны.