TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #1021 3.48K
WAT. История скобок, изменивших все
#опытным

Спасибо, @d7d1cd, за любезно предоставленный примерчик в рамках рубрики #ЧЗХ.

Посмотрите еще раз на этот пример:

int main()
{
std::vector<std::pair<std::string_view, std::string_view>> pairs
{
{{"one", "two"}, {"three", "four"}}
};

for (const auto & [f, s] : pairs)
{
std::cout << f << " and " << s << std::endl;
}
}


Вроде все тривиально, проще только 2+2. Все более менее с первого взгляда ожидают такой вывод:

one and two
three and four


Но у вашего компилятора на это другое мнение. Кланг, например, выводит:

one and three


WAT? А где 2 и 4? И почему вообще 1 элемент?

С виду вектор должен инициализироваться от std::initializer_list, в котором будут лежать 2 пары.

Но, судя по выводу, пара вообще одна. И здесь подсказка. Собака зарыта в одной лишней паре фигурных скобок:

/->/{/<-/{"one", "two"}, {"three", "four"}/->/}/<-/


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

Вот и получается, что строка выше парсится компилятором, как одна пара.

Тогда получается, что вью на строку можно создать с помощью {"one", "two"}?!?

Без проблем. Вот вам подходящий конструктор:

template< class It, class End >
constexpr basic_string_view( It first, End last );


У нас же строковые литералы неявно приводятся к указателям. Очень уж похоже на то, что мы хотим создать вью на непрерывный поток байтов. Компилятор именно это и предполагает. Жаль, что только:

The behavior is undefined if [first, last) is not a valid range


оба указателя не относятся к одной и той же последовательности, поэтому получили ub в наказание.

Кланг видимо идет от first либо до last, либо до символа конца строки. Поэтому 2 вьюхи содержат полные первые строки.

А вот gcc похоже идет до конца, пока не встретит last. Поэтому в его выводе куча мусора.

Пофиксить эту неприятную неожиданность можно либо убрав лишнюю пару скобок, либо явно сказав, где вы хотите видеть пары:

std::vector<std::pair<std::string_view, std::string_view>> pairs
{
{std::pair{"one", "two"}, std::pair{"three", "four"}}
};


Ну и да. Можно просто использовать С++17 и никакого уб не будет! В c++17 у std::string_view нет конструктора от двух итераторов, поэтому список {{"one","two"}, {"three","four"}} не мог быть использован для инициализации одного pair. Компилятор, следуя правилам инициализации из списка, развернул вложенный список и интерпретировал содержимое как два отдельных элемента для вектора. Можно убедиться тут. Спасибо @Shuomi за комментарий по поводу различного поведения при разных стандартах)

Avoid ambiguity. Stay cool.

#STL #cpp17 #cpp23
  • ❤ 31
  • 👍 14
  • 🤯 9
  • 🔥 7
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →