Единая инициализация в коде шаблона

Насколько я понимаю, равномерная инициализация является предпочтительным синтаксисом для инициализации объектов. Херб Саттер пишет

Во-первых, это называется "равномерная инициализация", потому что она, ну, в общем, одинаковая - одинакова для всех типов, включая составные структуры и массивы и 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 является типом класса, а список инициализаторов имеет один элемент типа cv U, где U является T или класс, полученный из T объект инициализируется из этого элемента (путем инициализации копирования для инициализации копирования списка или путем прямой инициализации для инициализации прямого списка).

Когда перегруженный конструктор разрешен, инициализация в скобках будет соответствовать конструктору с использованием параметров std:: initializer_list перед рассмотрением других перегруженных конструкторов. Так

bar c{b};

будет соответствовать конструктору, принимающему std:: initializer_list, а не сгенерированному конструктору копирования.

Это обсуждается в пункте 7: Различают () и {} при создании объектов Скотта Мейерса - Effective Modern C++.

Также Википедия - C++11 Единая инициализация

Другие вопросы по тегам