(Известная) ошибка компилятора в VC12?

Эта программа, скомпилированная с VC12 (в RTM RTU), [1] приводит к сбою (во всех конфигурациях сборки), когда это действительно не должно:

#include <string>

void foo(std::string const& oops = {})
{
}

int main()
{
    foo();
}

Я знаю о двух молчащих ошибках badegegen, которые связаны с может:

Честно говоря, я думаю, что это разные. Кто-нибудь знает

  • есть ли активная отслеживаемая ошибка для подключения для этого
  • существует ли способ обхода (или явное описание ситуации, которая вызывает эту ошибку, поэтому мы можем ее искать/избегать в нашей базе кода)?

[1] Просто создайте пустой проект, используя мастер-приложение консоли С++. Для простоты отключите предварительно скомпилированные заголовки и оставьте все значения по умолчанию: http://i.stack.imgur.com/rrrnV.png

Ответ 1

Активная проблема была опубликована в ноябрь. Проведенный пример кода:

Compile and run following code in VS2013

#include <string>

void f(std::string s = {}) {
}

int main(int argc, char* argv[]) {
    f();
    return 0;
}

Ошибка была подтверждена Microsoft.

Кажется, что там не работает обход. Изменить Обходные методы могут быть легко основаны на исключении синтаксиса list-initializer:

void f(std::string s = "");
void f(std::string s = std::string());
void f(std::string s = std::string {});

Или просто старомодный (если вы не возражаете против перегрузки):

void f(std::string s);
void f() { f(std::string()); }

Ответ 2

Похоже, что Visual Studio просто разбит относительно того, какой конструктор он вызывает, когда аргументом по умолчанию является список инициализаторов. Этот код:

#include <iostream>

struct test {
  test ()  { std::cout << "test ()" << std::endl ; } 
  test (int)  { std::cout << "test (int)" << std::endl ; }
};

void func( test const &s = {} )
{
}

int main()
{
    test s = {} ;
    func() ;
}

производит этот результат в gcc и clang, см. здесь.:

test ()
test ()

while Visual Studio производит этот результат:

test ()
test (int)

и для этого кода:

#include <iostream>
#include <initializer_list>

struct test {
  test ()  { std::cout << "test ()" << std::endl ; };

  test (int)  { std::cout << "test (int)" << std::endl ; };
  test ( std::initializer_list<int>) { std::cout << "test (initializer_list<int>)" << std::endl ; } ;
};

void func( test const &s = {0} )
{
}

int main()
{
    test s = {0} ;
    func() ;
}

gcc и clang произведите этот результат, см. жить здесь:

 test (initializer_list<int>)
 test (initializer_list<int>)

а Visual Studio вызывает эту ошибку:

 error C2440: 'default argument' : cannot convert from 'initializer-list' to 'const test &'
    Reason: cannot convert from 'initializer-list' to 'const test'
    No constructor could take the source type, or constructor overload resolution was ambiguous

Обновить

Для проверки работоспособности я вернулся к стандарту, чтобы убедиться, что в корне этой разницы не было какого-то странного правила или, возможно, какое-то ограничение, которое делает этот код плохо сформированным. Насколько я могу судить, этот код не плохо сформирован. Раздел 8.3.5 грамматика специально позволяет это:

parameter-declaration:
  attribute-specifier-seqopt decl-specifier-seq declarator
  attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
  [...]

Не похоже, что раздел 8.5 Инициализаторы или 8.3.6 Аргументы по умолчанию добавляют любые ограничения, но этот отчет о дефектах 994. braced-init-list как аргумент по умолчанию и рабочий документ Формулировка для инициализаторов символов в качестве аргументов по умолчанию дает понять, что это было предназначено и наметить изменения, внесенные в стандарт, чтобы позволить это и глядя на дельт, нет очевидных ограничений.