Почему синтаксис initalizer с двумя элементами помещает один элемент в вектор строк вместо двух? - PullRequest
3 голосов
/ 08 октября 2019

Почему я получаю один элемент в b вместо двух? В a я получаю один, как и ожидалось, а в c три элемента, как и ожидалось. Тот, у которого два значения, является каким-то особым случаем.

#include <string>
#include <vector>
#include <iostream>

void print(const std::string& name, const std::vector<std::string>& v)
{
    std::cout << name << ' ' << v.size() << '\n';
    for (const auto& str : v) {
        std::cout << str << '\n';
    }
    std::cout << "\n";
}

int main()
{
    std::vector<std::string> a = {{"str1"}};
    std::vector<std::string> b = {{"str1", "str2"}};
    std::vector<std::string> c = {{"str1", "str2", "str3"}};
    print("a", a);
    print("b", b);
    print("c", c);
    return 0;
}

Это печатает:

a 1
str1

b 1
str1

c 3
str1
str2
str3

Я думаю, это как-то связано с этой перегрузкой вектора ctor.

template< class InputIt >
vector( InputIt first, InputIt last,
    const Allocator& alloc = Allocator() );

Я использовал clang 9.0.0 с -std=c++17 -O2 -Wall в качестве флагов.

Что сделал компилятор в случае b? Почему он решил, что это итератор в одном случае и список инициализаторов в других случаях? Мой пример кода хорошо определен или имеет UB?

1 Ответ

4 голосов
/ 08 октября 2019

Мой пример кода хорошо определен или имеет UB?

У него есть UB. Конструктор, который принимает пару итераторов first - last, предполагает, что оба итератора ссылаются на одну и ту же последовательность. {{"str1"}, {"str2"}} не соответствует этому требованию, следовательно, UB.

Что сделал компилятор в случае b? Почему он решил, что это итератор в одном случае и список инициализаторов в других случаях?

Вспомните, что строковый литерал "str1" имеет тип char const[5]. Однако вызовы перегрузки конструктора для std::initializer_list работают с std::string экземплярами. Это требует неявного преобразования - и так как шаблон конструктора для двух итераторов не требует такого преобразования, это считается лучшим соответствием.


Это можно исправить, принудительно выполнив прямую инициализацию списка одной парой. фигурных скобок

std::vector<std::string> b2 = {"str1", "str2"};

или указав нужный тип по крайней мере один раз вручную:

std::vector<std::string> b3 = {{std::string("str1"), "str2"}};

using namespace std::string_literals;
std::vector<std::string> b4 = {{"str1"s, "str2"s}};
...