2012-06-24 5 views
4

Я немного смущен тем, что происходит, когда я копирую функторы и/или инициализаторы. В следующем коде я думал, что я буду копировать/перемещать объекты все время, но без него Segfaults. Кажется, я что-то делаю неправильно, но пока не понял, каковы мои неправильные предположения. Странно на cppreference.com Мне не удалось найти копию или перемещение конструктора для initializer_list, поэтому мне интересно, что на самом деле происходит в этих случаях.Копия функтора и initializer_list

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

std::initializer_list<std::function<std::string()>> getInitializer() { 
    return { 
    []() -> std::string { 
     return "If"; 
    } 
    }; 
} 

int main() { 
    std::function<int(std::string)> func; 
    { 
     auto init = getInitializer(); 

     func = [init](std::string text) -> int { 
      std::vector<std::function<std::string()>> vec(init); 

      for(auto& el : vec) { 
       std::cout << el(); 
      } 
      std::cout << text << std::endl; 
      return 5; 
     }; 
    } 

    return func(" you see this - the world is all right!"); 
} 
+0

Почему бы не добавить 'using' заявление на вершине? –

+0

@ColeJohnson 'использование' для чего? – abergmeier

+0

@LCIDFire 'using namespace std;' поэтому вам не нужно набирать 'std ::' все время. http://msdn.microsoft.com/en-us/library/aewtdfs3(v=vs.100).aspx –

ответ

7

У меня нет большого опыта работы с initializer_list с, но стандартом, кажется, предполагает, что реализация initializer_list как если его пару указателей на массив. Список в getInitializer имеет автоматический срок службы, а также массив, который его поддерживает. В итоге вы возвращаете пару указателей в массив, который больше не существует.

Соответствующие разделы стандарта 8.5.4 [decl.init.list] пункты 5 и 6:

5.- Объект типа std::initializer_list<E> строится из списка инициализатора, как если реализация выделен массив из N элементы типа E, где N - количество элементов в списке инициализаторов. Каждый элемент этого массива инициализируется копией с соответствующим элементом списка инициализаторов, а объект std::initializer_list<E> построен для обращения к этому массиву. Если для инициализации любого из элементов требуется сужение преобразования, программа плохо сформирована.

6.- Срок службы массива такой же, как у объекта initializer_list.


Так что для вашего конкретного случая, реализация будет примерно эквивалентно следующему:

std::initializer_list<std::function<std::string()>> getInitializer() { 
    std::function<std::string()> __a[1] = { 
    []() -> std::string { 
     return "If"; 
    } 
    }; 
    return std::initializer_list<std::function<std::string()>>(__a, __a+1); 
} 
+0

Я немного смущен. Почему они решили реализовать 'std :: initializer_list' с массивом памяти? Почему бы не пойти и не основывать его на 'std :: array'? Для меня этот класс полностью нарушен, потому что обычное использование контейнера не работает. – abergmeier

+0

@LCIDFire 'std :: array' здесь не проблема (ведь реализация на самом деле ее использует). SC решил ограничить время жизни объектов, на которые делается ссылка, чтобы сделать «std :: initializer_list» легким и дешевым для копирования. Он эффективно имеет ссылочную семантику, которая, вы правы, * необычна для C++. 'std :: initializer_list' не предназначался как контейнер, вы должны его вернуть. –

+0

@LCID Fire: Почему вместо этого вы просто используете 'std :: array'? –

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