2012-01-09 5 views
45

Следующий код:Почему emplace_back() не использует равномерную инициализацию?

#include <vector> 

struct S 
{ 
    int x, y; 
}; 

int main() 
{ 
    std::vector<S> v; 
    v.emplace_back(0, 0); 
} 

дает следующие ошибки при компиляции с GCC:

In file included from c++/4.7.0/i686-pc-linux-gnu/bits/c++allocator.h:34:0, 
       from c++/4.7.0/bits/allocator.h:48, 
       from c++/4.7.0/vector:62, 
       from test.cpp:1: 
c++/4.7.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = S; _Args = {int, int}; _Tp = S]': 
c++/4.7.0/bits/alloc_traits.h:265:4: required from 'static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]' 
c++/4.7.0/bits/alloc_traits.h:402:4: required from 'static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>]' 
c++/4.7.0/bits/vector.tcc:97:6: required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, int}; _Tp = S; _Alloc = std::allocator<S>]' 
test.cpp:11:24: required from here 
c++/4.7.0/ext/new_allocator.h:110:4: error: new initializer expression list treated as compound expression [-fpermissive] 
c++/4.7.0/ext/new_allocator.h:110:4: error: no matching function for call to 'S::S(int)' 
c++/4.7.0/ext/new_allocator.h:110:4: note: candidates are: 
test.cpp:3:8: note: S::S() 
test.cpp:3:8: note: candidate expects 0 arguments, 1 provided 
test.cpp:3:8: note: constexpr S::S(const S&) 
test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'const S&' 
test.cpp:3:8: note: constexpr S::S(S&&) 
test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'S&&' 

vector Предложив использует синтаксис регулярного () конструктора для построения элемента из аргументов emplace_back(). Почему вместо vector использовать синтаксис унифицированной инициализации {}, чтобы привести примеры, подобные описанным выше?

Мне кажется, что нечего терять, используя {} (он вызывает конструктор, когда он есть, но все еще работает, когда его нет), и это было бы больше в духе C++ 11 использовать {} - в конце концов, вся точка равномерная Инициализация заключается в том, что она используется равномерно - то есть везде - для инициализации объектов.

ответ

42

Великие умы думают одинаково; v). Я представил отчет о дефектах и ​​предложил изменить стандарт на эту тему.

http://cplusplus.github.com/LWG/lwg-active.html#2089

Кроме того, Люк Дантон помог мне понять трудности: Direct vs uniform initialization in std::allocator.

Когда EmplaceConstructible (23.2.1 [container.requirements.general]/13) Требование используется для инициализации объекта, происходит прямой инициализации. Инициализация агрегата или с использованием конструктора std :: initializer_list с emplace требует именования инициализированного типа и перемещения временного. Это результат std :: allocator :: construct using direct-initialization, а не list-initialization (иногда называемая «равномерной инициализацией») синтаксис.

изменяющего станд :: Распределитель :: сооружать использовать список инициализацию бы, среди прочего, отдавать предпочтение StD :: initializer_list конструктор перегрузок, нарушая правильный код в неинтуитивных и невозможно исправить пути - не было бы путь для emplace_back для доступа к конструктору , предварительно вытесненному std :: initializer_list, без по существу переопределения push_back.

std::vector<std::vector<int>> v; 
v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization 

Предлагаемый компромисс заключается в использовании SFINAE с стандом :: is_constructible, которая проверяет, является ли хорошо сформирована прямой инициализация. Если is_constructible является ложным, тогда выбирается альтернатива std :: allocator :: construct overload, которая использует list-initialization. Поскольку инициализация списка всегда возвращается к прямой инициализации , пользователь будет видеть диагностические сообщения, как если бы инициализация списка (равномерная инициализация) всегда использовалась, , потому что перегрузка с прямой инициализацией не может завершиться неудачей.

Я вижу два угловых корпуса, которые открывают пробелы в этой схеме. Один из них встречается , когда аргументы, предназначенные для std :: initializer_list, удовлетворяют конструктору , например, попытке вставления - вставить значение {3, 4} в пример выше .Обходным путем является явно указывать тип std :: initializer_list , как в v.emplace_back (std :: initializer_list (3, 4)). Так как это соответствует семантика, как если бы был выведен std :: initializer_list, похоже, не будет настоящей проблемой.

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

+2

Можно было бы придумать тип языка supporrt для идеально передовых списков инициализации синтаксических фигурных скобок. Тогда можно просто сказать emplace_back ({a, b, ...}) –

+4

@ JohannesSchaub-litb: IMHO, это больше похоже на то, что должно было быть 'std :: initializer_list'. Но простая модификация 'std :: allocator' должна исправлять все в порядке. – Potatoswatter

+0

Что делать, если thevuser хочет сместить вектор с пользовательским распределением и записать emplace_back (1,2, alloc). Как это известно, чтобы передать его как ({1,2}, alloc)? imo, что раздатчик взлома разрывает вещь ecen больше. необходимо проработать надлежащее решение. –

 Смежные вопросы

  • Нет связанных вопросов^_^