Соответствует ли объявление с использованием "auto" объявлению extern, которое использует конкретный спецификатор типа?


рассмотрим следующую программу:

extern int x;
auto x = 42;
int main() { }

Clang 3.5 принимает его (демо), GCC 4.9 и VS2013 не делают (демо-версия для бывших). Кто прав, и где правильное поведение, указанное в стандарте C++?

3 54

3 ответа:

там удивительно мало в стандарте об этом. Все, что мы слышим о повторном объявлении:

[C++11: 3.1/1]: объявление (пункт 7) может вводить одно или несколько имен в единицу перевода или повторно объявлять имена, введенные предыдущими объявлениями. [..]

и единственная соответствующая часть auto'ы семантики:

[C++11: 7.1.6.4/3]: в противном случае тип переменной выводится из его initializer. [..]

(напоминая нам, что типа x - это int).

мы знаем, что переменная должна иметь один и тот же тип во всех объявлениях:

[C++11: 3.5/10]: после всех корректировок типов (в ходе которых typedefs (7.1.3) заменяются их определениями), типы, указанные во всех объявлениях, ссылающихся на данную переменную или функцию, должны быть идентичны, за исключением того, что объявления массив объект может указывать типы массивов, которые отличаются наличием или отсутствием привязки к основному массиву (8.3.4). Нарушение этого правила для идентификации типа не требует диагностики.

и "после всех корректировок типов" должны заботиться о любых вопросах, касающихся autoучастия во всем этом; моя интерпретация заключается в том, что это по своей сути допустимое повторное объявление (и определение)x в глобальной области видимости типа int, и что Clang правильно. Даже если мы предложим это auto не считается "настройка типа", так как никакая диагностика не требуется, в худшем случае все перечисленные реализации совместимы по-своему.

я считаю, что GCC и Visual Studio берут в качестве вдохновения следующее:

[C++11: 7.1.6.4/5]: программа, которая использует auto в контексте, явно не разрешенном в этом разделе, плохо сформирован.

...но я думаю, что это недальновидно. Кажется маловероятным, что стандартный язык предназначен для запрета обычных правил повторного объявления только потому, что они не повторяются или явно ссылаются изнутри 7.1.6.4.

в C++14 добавляет формулировку, которая относится к заявлениям функции с выведенными типа:

[C++14: 7.1.6.4/13]: также должны использоваться повторные объявления или специализации функции или шаблона функции с объявленным типом возвращаемого значения, который использует тип заполнителя это заполнитель, а не выводимый тип. [..]

по симметрии можно было бы предположить, что в вашем int case, предполагается, что GCC и VS будут правильными в отклонении программы. Однако это другая особенность (поскольку вычет не может быть применен к простым объявлениям) и, следовательно, другой сценарий.

в любом случае, улучшенная стандартная формулировка поможет здесь. Я считаю это [достаточно незначительным] редакционным дефектом.

Я представляю себе ограничение в [dcl.спекуляция.авто]Р11 существует, потому что в противном случае это позволило бы:

int f();
auto f(); // What's the return type here?

дело в том, что у вас может быть необразованный тип типа имеет возвращаемый тип функции. Нет никаких правил вычета, основанных на предыдущих объявлениях, поэтому такое смешивание запрещено для функций, хотя следующее Было бы прекрасно:

int f();
auto f() { return 1; }

эта проблема не существует для переменных:

extern int v;
extern auto v; // ill-formed

любой переменные только для объявления должны использовать тип без заполнителя. Это означает, что если вы используете тип заполнителя для определения v, он может быть выведен без каких-либо проблем, а затем, конечно, должен соответствовать типу без заполнителя, используемому в первом объявлении.

extern int v;
auto v = 1; // ok, type deduced as 'int', matches first declaration.

Примечание

Я ответил вопрос, который был закрыт дубликат этого. Я попросил слияния и вместо этого сказал ответьте здесь. Ниже мой оригинальный ответ.

обновление clang правильно

Я задал этот вопрос на twitter и ответ я получил от Ричарда Смита следующим образом:

не дефект, это намеренно, что этот ограничение применяется только к выводимым типам возвращаемых значений, а не к переменным типам. Для переменных это просто удобная стенография, но дедукция возвращаемого типа влияет на что-то более фундаментальное в функциях (и особенно в шаблонах функций).

так что логика в том, что это разрешено [dcl.spec.auto] и ограничить это для выведенных возвращаемых типов [dcl.spec.auto]p11 был добавлен в противном случае нет никаких ограничений, и поэтому это не ограничено для переменных случай.

Оригинал

в настоящее время [dcl.spec.auto] Не кажется, чтобы охватить этот случай явно, но он говорит в [dcl.спекуляция.авто]Р5:

программа, которая использует авто или decltype(авто) в контексте явно не разрешены в этот раздел является некорректным.

и мы видим, что это делает аналогичный случай для функций, плохо сформированных в [dcl.спекуляция.авто]Р11:

Redeclarations или специализации функции или шаблона функции с объявленным типом возврата, который использует тип заполнителя также используйте этот заполнитель, а не выводимый тип. Аналогично, повторные объявления или специализации функции или шаблона функции с объявленным тип возврата, который не использует тип заполнителя не должен использовать a заполнитель. [ Пример:

auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()

....

так что хотя это может использовать разъяснение как в настоящее время сформулировано, похоже, что gcc является правильным, и это плохо сформировано.