Директната инициализация на списък се компилира успешно, но нормалната директна инициализация е неуспешна, защо?

Например кодирайте така:

struct A { A(int); };
struct B { B(A);   };

int main()
{
    B b{{0}}; // OK
    B c({0}); // error
}

Съобщенията за грешка са:

f.cc: In function 'int main()':
f.cc:7:9: error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
  B c({0}); // error

         ^
f.cc:7:9: note: candidates are:
f.cc:2:12: note: B::B(A)
 struct B { B(A);   };
        ^
f.cc:2:8: note: constexpr B::B(const B&)
 struct B { B(A);   };
        ^
f.cc:2:8: note: constexpr B::B(B&&)

person Jack Chin    schedule 09.09.2015    source източник
comment
Да, трябва да е публично. Промених това, благодаря!   -  person Jack Chin    schedule 09.09.2015
comment
Изненадващо, това се проваля, дори когато изтрия конструктори за копиране и преместване на B: coliru.stacked-crooked. com/a/7fb30cd9d5839ac2 Съобщението за грешка при компилиране е доста объркващо, то е двусмислено с изтритите функции.   -  person Chris Beck    schedule 09.09.2015
comment
@ChrisBeck изтритите функции участват в разрешаването на претоварване, така че не е объркващо, че изтриването им няма значение   -  person M.M    schedule 09.09.2015
comment
B b({{{0}}}); Това се компилира, нямам представа защо. Вуду може би?   -  person Fibbs    schedule 09.09.2015
comment
Мисля, че формата на скоби за конструиране на {} никога не предполага списък с инициализатор, но версията на родител () винаги го прави, дори когато няма конструктор на списък с инициализатор. Този отговор дава известна информация, но не и окончателен отговор. stackoverflow.com/questions/29851502/   -  person Galik    schedule 09.09.2015


Отговори (1)


От последния официален стандарт, C++14, първата ви инициализация не е двусмислена. [over.match.list]:

въведете описание на изображението тук

Тъй като не съществуват конструктори на инициализатор-списък, ние влизаме във "втората фаза". А сега разгледайте [over.best.ics]/4:

въведете описание на изображението тук

Нашият елемент е {0}. Следователно това не позволява (дефинирано от потребителя) преобразуване {0} -> A за конструктора за копиране. Ясно е, че това не се прилага, ако не сме във втората фаза на [over.match.list], така че за вашия пример с B c({0}), не възниква инициализация на списък за c и се вземат предвид и двата конструктора.


CWG брой 1467

Първата инициализация в момента е също толкова двусмислена, колкото и втората. Компилаторите просто не са внедрили CWG #1467< /strong> все още - неговата резолюция премахна точка (4.5), цитирана по-горе.
Вижте #2076, който избира да отмени промяната:

Резолюцията на проблем 1467 направи някои правдоподобни конструкции зле оформени. Например,

struct A { A(int); };
struct B { B(A); };
B b{{0}};

Това вече е двусмислено, тъй като текстът, забраняващ дефинирани от потребителя преобразувания за конструктори за копиране и преместване на B, беше премахнат от 13.3.3.1 [over.best.ics] параграф 4.

„Текстът“ е гореспоменатата точка. Ричард Смит предлага следната формулировка:

За типове, които не са класове, ние позволяваме инициализация от списък с единичен елемент за извършване на копие само ако елементът в списъка сам по себе си не е списък (13.3.3.1.5 [over.ics.list] bullet 9.1). Аналогичното правило за този случай би било да се добави отново водещият символ в 13.3.3.1 [over.best.ics] параграф 4, но само в случай, че самият инициализатор е списък с инициализатор:

втората фаза на 13.3.1.7 [over.match.list], когато списъкът с инициализатор има точно един елемент, който сам по себе си е списък с инициализатор, където целта е първият параметър на конструктор
от клас X, а преобразуването е до X или препратка към (евентуално cv-квалифициран) X,

Тъй като инициализаторът {0} сам по себе си е списък с инициализатори, този куршум ще направи първата ви инициализация отново добре оформена.

person Columbo    schedule 09.09.2015