2016-04-02 2 views
10

Допустим, у вас есть переменная типа std::vector<std::string> и инициализации его со списком инициализатора:Как перемещать элементы initializer_list?

using V = std::vector<std::string>; 
V v = { "Hello", "little", "world", "of", "move", "semantics" }; 

компилятор создаст временный std::string для каждой строки буквального, создать список инициализатора над ними, а затем вызвать CTOR для V и создайте вектор. Ctor не знает, что все эти строки являются временными, поэтому это копия каждой строки.

Я не нашел ничего в стандарте, который позволяет вектору ctor перемещать элементы, когда они являются временными.

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

+0

Хороший вопрос. Я не думаю, что списки инициализаторов изначально были предназначены *, чтобы делать что-либо, но передавать последовательность времен. Но теперь вы можете объявить переменную списка инициализатора. Это не означает, что вектор должен высасывать внутренности из каждого элемента в таком длинном списке. Но подождите, конструктор с rvalue ref в список инициализаторов. Может быть? –

+0

@ Cheersandhth.-Alf Я думаю, я могу перегрузить список инициализаторов по const-ref, non-const-ref и rvalue-ref. Затем я могу вручную перемещать элементы из списка инициализаторов rvalue-ref. Не элегантный и не работает с контейнерами STL, которые я использую в качестве основного хранилища (вектор и карта), но лучше, чем ничего. Благодарю. –

+0

дубликат [initializer \ _list and move semantics] (https://stackoverflow.com/questions/8193102/initializer-list-and-move-semantics) –

ответ

4

Там нет никакого способа, чтобы избежать копирований из initializer_list<string>, поскольку стандарт определяет вызов конструктора принимающего список инициализатора аргумент, из фигурных скобок инициализатора в качестве фактического аргумента, следующим образом (подчеркивание добавлено):

C++ 14 §8.5.4/5

объект типа std::initializer_list<E> строится из списка инициализатора, как если реализация выделяется временный массив N элементов типа const E, где N является количество элементов в списке инициализатора

ИМХО это действительно печально.

Обходной путь (для ваших собственных занятий) заключается в принятии initializer_list<char const*>.


Вот пример обхода применяется к std::vector<string>. Для этого, когда вы не контролируете код класса, это подразумевает объявление массива данных (фактически initializer_list) явно. Это так же, как с C++ 03, который механизм списка инициализаторов был предназначен, чтобы избежать:

#include <vector> 
#include <initializer_list> 
#include <iostream> 
#include <iterator>    // std::begin, std::end 
using namespace std; 

struct My_string 
{ 
    char const* const ps; 

    My_string(char const* const s) 
     : ps(s) 
    { 
     cout << " My_string(*) <- '" << s << "'" << endl; 
    } 

    My_string(My_string const& other) 
     : ps(other.ps) 
    { 
     cout << " My_string(const&) <- '" << other.ps << "'" << endl; 
    }; 

    My_string(My_string&& other) 
     : ps(other.ps) 
    { 
     cout << " My_string(&&) <- '" << other.ps << "'" << endl; 
    }; 
}; 

auto main() -> int 
{ 
    cout << "Making vector a." << endl; 
    vector<My_string> const a = {"a1", "a2", "a3"}; 
    cout << "Making data for vector b." << endl; 
    auto const b_data   = { "b1", "b2", "b3" }; 
    cout << "Making vector b." << endl; 
    vector<My_string> const b(begin(b_data), end(b_data)); 
} 

Выход:

 
Making vector a. 
    My_string(*) <- 'a1' 
    My_string(*) <- 'a2' 
    My_string(*) <- 'a3' 
    My_string(const&) <- 'a1' 
    My_string(const&) <- 'a2' 
    My_string(const&) <- 'a3' 
Making data for vector b. 
Making vector b. 
    My_string(*) <- 'b1' 
    My_string(*) <- 'b2' 
    My_string(*) <- 'b3' 
+0

Вы также можете использовать конструктор вариационных шаблонов. – o11c

+0

'const E'? Спасибо, действительно * неудачно. Есть ли причина для этого? В противном случае обнаружение списка инициализаторов rvalue по крайней мере позволит мне перемещаться вручную, но с 'const E', что кажется незаконным !? (И я не использую 'std :: string', я использую тип, который я написал, который может быть дороже для копирования) –

+0

Я думаю, что нашел решение с помощью' mutable', я добавил автоответ. Что вы думаете об этом? –

2

После некоторого мышления, я придумал решение, основанное на mutable. Другой ответ по-прежнему в основном правильный, но можно создать прокси с изменяемым членом, чтобы избавиться от верхнего уровня const -ness, а затем переместить элементы оттуда. Поэтому методы, использующие список инициализаторов, должны перегружать список инициализаторов const-ref и версию rvalue-ref, чтобы знать, когда им разрешено перемещаться.

Вот пример работы, он может выглядеть произвольно сначала, но в моем реальном случае использования, он решил проблему.

#include <iostream> 
#include <vector> 

// to show which operations are called 
struct my_string 
{ 
    const char* s_; 
    my_string(const char* s) : s_(s) { std::cout << "my_string(const char*) " << s_ << std::endl; } 
    my_string(const my_string& m) : s_(m.s_) { std::cout << "my_string(const my_string&) " << s_ << std::endl; } 
    my_string(my_string&& m) noexcept : s_(m.s_) { std::cout << "my_string(my_string&&) " << s_ << std::endl; } 
    ~my_string() { std::cout << "~my_string() " << s_ << std::endl; } 
}; 

// the proxy 
struct my_string_proxy 
{ 
    mutable my_string s_; 

    // add all ctors needed to initialize my_string 
    my_string_proxy(const char* s) : s_(s) {} 
}; 

// functions/methods should be overloaded 
// for the initializer list versions 

void insert(std::vector<my_string>& v, const std::initializer_list<my_string_proxy>& il) 
{ 
    for(auto& e : il) { 
     v.push_back(e.s_); 
    } 
} 

void insert(std::vector<my_string>& v, std::initializer_list<my_string_proxy>&& il) 
{ 
    for(auto& e : il) { 
     v.push_back(std::move(e.s_)); 
    } 
} 

int main() 
{ 
    std::vector<my_string> words; 
    insert(words, { {"Hello"}, {"initializer"}, {"with"}, {"move"}, {"support"} }); 
} 

Live example

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