Какие жадные примеры списка инициализаторов скрываются в стандартной библиотеке?

Так как С++ 11, контейнеры стандартной библиотеки и std::string имеют конструкторы, которые берут список инициализаторов. Этот конструктор имеет приоритет над другими конструкторами (даже, как отметил @JohannesSchaub-litb в комментариях, даже игнорируя другие критерии "наилучшего соответствия" ). Это приводит к нескольким известным ловушкам при преобразовании всех форм в квадратных скобках () конструкторов в их скобки версии {}

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <string>

void print(std::vector<int> const& v)
{
    std::copy(begin(v), end(v), std::ostream_iterator<int>(std::cout, ","));
    std::cout << "\n";
}

void print(std::string const& s)
{
    std::cout << s << "\n";
}

int main()
{
    // well-known 
    print(std::vector<int>{ 11, 22 });  // 11, 22, not 11 copies of 22
    print(std::vector<int>{ 11 });      // 11,     not 11 copies of 0

    // more surprising
    print(std::string{ 65, 'C' });      // AC,     not 65 copies of 'C'
}

Я не смог найти третий пример на этом сайте, и эта вещь возникла в чате Lounge < С++ > (при обсуждении с @rightfold, @Abyx и @JerryCoffin). Несколько удивительная вещь заключается в том, что преобразование std::string, использующий счетчик и символ для использования {} вместо (), изменяет его значение из n копий символа на символ n -th (обычно из таблицы ASCII), за которым следует другой характер.

Это не улавливается обычным запретом на привязку к сужению конверсий, потому что 65 является постоянным выражением, которое может быть представлено как char и будет сохранять свое исходное значение при преобразовании обратно в int (§8.5.4/7, bullet 4) (спасибо @JerryCoffin).

Вопрос: есть ли еще примеры, скрывающиеся в стандартной библиотеке, где преобразование конструктора стиля () в стиль {}, с жадностью совпадает с конструктором списка инициализаторов?

Ответ 1

Я предполагаю, что с вашими примерами для std::vector<int> и std::string вы должны также охватывать другие контейнеры, например std::list<int>, std::deque<int> и т.д., которые имеют одинаковую проблему, очевидно, как std::vector<int>, Аналогично, int не является единственным типом, так же как и для char, short, long и их версии unsigned (возможно, еще нескольких интегральных типов).

Я думаю, что есть также std::valarray<T>, но я не уверен, что T разрешено быть целым типом. На самом деле, я думаю, что они имеют различную семантику:

std::valarray<double>(0.0, 3);
std::valarray<double>{0.0, 3};

Есть несколько других стандартных шаблонов классов С++, которые принимают аргумент std::initializer_list<T>, но я не думаю, что у любого из них есть перегруженный конструктор, который будет использоваться при использовании скобок вместо скобок.

Ответ 2

Просто поиск появления initializer_list.

  • Все последовательности, они имеют конструкторы, подобные конструкторам:

    • deque
    • dynarray
    • forward_list
    • список
    • вектор
  • valarray

  • basic_string

  • Неупорядоченные коллекции, есть конструктор, который принимает целое число для определения начального количества ведро.

    • unordered_set
    • unordered_multiset

Я думаю, что все это.

#include <unordered_set>
#include <iostream>

int main() {
    std::unordered_set<int> f (3);
    std::unordered_set<int> g {3};
    std::cout << f.size() << "/" << g.size() << std::endl; // prints 0/1.
}