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

Начиная с C++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-ый символ (обычно из таблицы ASCII), за которым следует другой символ.

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

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

2 ответа

Решение

Я полагаю, с вашими примерами для 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};

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

Просто в поисках появления 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.
}
Другие вопросы по тегам