2015-04-03 5 views
1

Я делаю небольшой класс, который использует массив, построенный на основе его размера. Вот код ...C++ 11 инициализация массива не вызовет конструктор копирования

.hpp

template <size_t N> 
class KeyCombinationListener 
{ 
public: 
    KeyCombinationListener(
     const std::array<sf::Keyboard::Key, N>& sequence, 
     std::function<void (void)> fn 
     ); 

private: 
    std::array<sf::Keyboard::Key, N> combo; 
    std::function<void (void)> callback; 
}; 

.cc

template <size_t N> 
KeyCombinationListener<N>::KeyCombinationListener(
    const array<sf::Keyboard::Key, N>& sequence, function<void (void)> fn 
    ) : combo(sequence), progress{begin(combo)}, callback{fn} 
{ 

} 

При инициализации члена конструктора, я не могу использовать combo{sequence} как инициализаторе, потому что он принимает только sf::Keyboard::Key типы. Это имеет смысл, если он просит initializer_list, но это кажется мне странным. С другими стандартными контейнерами я могу вызвать конструктор копирования, используя {} нотацию только отлично. Это причуда с std::array? Или, может быть, ошибка в моем звоне?

Только в случае, если это поможет, вот моя версия лязг:

Debian clang version 3.5.0-10 (tags/RELEASE_350/final) (based on LLVM 3.5.0) 
Target: x86_64-pc-linux-gnu 
Thread model: posix 
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9 
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9.2 
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.9 
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.9.2 
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/4.9 
Candidate multilib: .;@m64 
Selected multilib: .;@m64 
+1

Это причуда 'std :: array' и дефект в C++ 14. Этот контейнер должен быть агрегатом, а инициализация списка с отдельными элементами была дефектной в C++ 14. См. Http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1467 – dyp

+0

@ dyp Хорошо, спасибо. Означает ли это, что я могу ожидать увидеть исправление в C++ 17? –

+0

Предлагаемая резолюция содержится в последних черновиках, поэтому я ожидаю, что она будет исправлена ​​в следующей редакции стандарта. – dyp

ответ

2

Вы столкнулись с дефектом в C++: список инициализации из одного элемента. Поведение, указанное в стандарте C++ 11 и C++ 14, вызывает удивление. Я буду ссылаться на C++ 14 ниже.

Шаблонные экземпляры std::array являются агрегатами [array.overview]/2. Таким образом, при инициализации std::array объектов из рамно-инициализации-листа, агрегатно-инициализация будет выполняться без разбора из числа инициализаторов [dcl.init.list] /3.1. Другие классы контейнеров не могут быть агрегатами из-за требований для определенных конструкций (например, из пары итераторов).

Агрегатная инициализация инициализирует (потенциально рекурсивно) элементы данных из инициализаторов. В вашем случае он попытается инициализировать первый элемент данных из std::array<sf::Keyboard::Key, N> из инициализатора sequence (который имеет тот же тип). Для всех реализаций std::array Я знаю, что первый член данных std::array является массивом типа C. Затем инициализация списка попытается инициализировать первый элемент этого массива из исходного инициализатора: sequence.

Пример:

struct aggregate 
{ 
    int m[2]; 
}; 

aggregate x = {0, 1}; 
assert(x.m[0] == 0 && x.m[1] == 1); 

aggregate y{x}; // error: cannot convert `aggregate` to `int` 

инициализации в последней строке будет пытаться инициализировать y.m[0] от x.


CWG issue 1467 описывает это и связанный вопрос, список инициализации, когда нет Инициализаторы. Предлагаемая резолюция вводит (еще один) специальный случай для инициализации списка, который охватывает проблему в ОП. Цитируя недавний проект github, [dcl.init.list]/3.1

Если T является типом класса и список инициализатора имеет единственный элемент типа резюмеU, где U является T или класса, производным от T, объекта инициализируется из этого элемента (по копирование-инициализация для инициализация списка копий или прямая инициализация для инициализация прямых списков).

Агрегатная инициализация в последних черновиках имеет более низкий «приоритет» (3.3), то есть будет выполняться только в том случае, если указанное выше условие не выполняется.


Последние версии г ++ (5,0) и лязг ++ (3.7.0) осуществления предлагаемой резолюции даже в режиме C++ 11.