Единая инициализация в коде шаблона
Насколько я понимаю, равномерная инициализация является предпочтительным синтаксисом для инициализации объектов. Херб Саттер пишет
Во-первых, это называется "равномерная инициализация", потому что она, ну, в общем, одинаковая - одинакова для всех типов, включая составные структуры и массивы и std:: container...
и принятый ответ на этот вопрос гласит
Предпочитайте {} инициализацию альтернативам, если у вас нет веских причин не делать этого.
Однако рассмотрим этот код:
#define BRACES
template<typename V>
class foo {
public:
template<typename W>
explicit foo(const W &w) :
#ifdef BRACES
m_v{w}
#else // #ifdef BRACES
m_v(w)
#endif // #ifdef BRACES
{}
private:
V m_v;
};
struct bar{};
int main()
{
bar b;
foo<bar>{b};
#ifdef BRACES
bar c{b};
#else // #ifdef BRACES
bar c(b);
#endif // #ifdef BRACES
}
Если #define BRACES
без комментариев, этот код не может быть собран (g++ 4.8.5) с error: too many initializers for ‘bar’
на линии
m_v{w}
в конструкторе foo
, Это имеет смысл, так как более прямой вызов
bar c{b};
в main
происходит сбой аналогичным образом, и они по сути одинаковы (хотя код шаблона этого не знает).
И наоборот, комментируя #define BRACES
заставляет все строить. Является ли это показателем, чтобы избежать этой формы инициализации в этом типе кода шаблона?
редактировать
@melak47 указал, что эта проблема отсутствует в g ++5.1, и дал убедительное доказательство. По-видимому, он исчез где-то между 4.8.5 и 5.1.
2 ответа
Инициализация списков не совсем работает, когда вы пытаетесь инициализировать агрегат из чего-то того же типа.
Это CWG 1467, чья резолюция (среди прочего) добавила еще один маркер в гигантский список в [dcl.init.list]/3, чтобы сделать эту работу:
Инициализация списка объекта или ссылки типа
T
определяется следующим образом:
- Если
T
является типом класса, а список инициализаторов имеет один элемент типа cvU
, гдеU
являетсяT
или класс, полученный изT
объект инициализируется из этого элемента (путем инициализации копирования для инициализации копирования списка или путем прямой инициализации для инициализации прямого списка).
Когда перегруженный конструктор разрешен, инициализация в скобках будет соответствовать конструктору с использованием параметров std:: initializer_list перед рассмотрением других перегруженных конструкторов. Так
bar c{b};
будет соответствовать конструктору, принимающему std:: initializer_list, а не сгенерированному конструктору копирования.
Это обсуждается в пункте 7: Различают () и {} при создании объектов Скотта Мейерса - Effective Modern C++.