Почему это является прямой декларацией в С++?
У меня будет следующий фрагмент кода в файле utilA.cpp:
// utilB.h
namespace xm
{
void zoo(struct tm timeval); //<-----line 0
}
// utilA.cpp
#include <utilB.h> //<----line 1
#include <time.h> //<----line 2
namespace xm
{
void foo()
{
struct tm time1 = {0}; //<----line 3
}
}
GCC жалуется при компиляции utilA.cpp,
error: variable 'xm::tm time1' has initializer but incomplete type
Похоже, это потому, что utilA.h
использует struct tm
в строке 0, но не включает time.h
, а компилятор обрабатывает struct tm
в строке 0 в качестве прямого объявления, поэтому struct tm
в строке 2 разрешается как xm::tm
внутри заголовка в строке 0.
Значит, стандарт С++ определяет этот struct tm
как тип параметра функции как форвардное объявление? Пожалуйста, помогите объяснить это, и цитаты из стандарта будут полезны.
Ответы
Ответ 1
В строке 0 вы объявили класс с именем tm
внутри пространства имен xm
. Да, С++ позволяет объявлять типы в параметрах функции/шаблона.
N4140 § 3.4.4 [basic.lookup.elab]/2
Если специфицированный тип-спецификатор вводится ключом класса и этот поиск не находит ранее объявленное имя типа, или если специфицированный тип-спецификатор появляется в объявлении с формой:
идентификатор атрибута-спецификатора класса-seq opt;
специфицированный спецификатор типа - это объявление, которое вводит class-name, как описано в разделе 3.3.2.
Поскольку вы объявили класс с именем tm
внутри пространства имен xm
, это первое имя, которое находит поиск имени для tm
в строке 3. ::tm
(и ::std::tm
) не рассматриваются. И поскольку нет определения класса ::xm::tm
, компилятор жалуется на то, что он является неполным типом.
Если вы не пишете C-код на С++, вы должны написать что-то вроде 1
struct tm;
namespace xz{
void zoo(tm timeval);
}
или
#include <ctime>
namespace xz{
void zoo(tm timeval);
}
и у вас не было бы этой проблемы.
1 помните, что вы не можете перенаправлять объявления в пространстве имен std
Ответ 2
Итак, стандарт С++ определяет этот struct tm
как тип параметра функции как форвардное объявление. Пожалуйста, помогите объяснить это, и квота из стандарта будет полезна.
Да, struct tm timeval
вводит здесь новое имя класса xm::tm
.
(пояснения и цитаты)
struct tm
является специфицированным спецификатором типа, который может быть использован для представления нового имени класса.
$3.1/4 Объявления и определения [basic.def]
[Примечание. Имя класса также может быть объявлено неявным спецификатором разработанного типа ([dcl.type.elab]). - конечная нота]
$9.1/2 Имена классов [class.name]:
Объявление, состоящее исключительно из идентификатора ключа класса; является либо переоформление имени в текущем поле или вперед объявление идентификатора в качестве имени класса. Он вводит класс name в текущую область.
$3.4.4/2 Специфичные спецификаторы типов [basic.lookup.elab]:
или если специфицированный спецификатор типа появляется в объявлении с форма:
class-key attribute-specifier-seqopt identifier ;
специфицированный спецификатор типа - это объявление, которое вводит class-name, как описано в [basic.scope.pdecl].
$3.3.2/7 Точка декларации [basic.scope.pdecl]:
если специфицированный спецификатор типа используется в описании-спецификаторе-seq или параметр-объявление-предложение функции, определенной в области пространства имен, идентификатор объявляется как имя класса в пространстве имен, содержит объявление;
Для struct tm timeval
, используемого в качестве объявления параметра функции, поскольку <time.h>
не включен и класс класса tm
по-прежнему отсутствует, класс tm
будет объявлен в текущей области (т.е. namespace xm
), затем xm::tm
будет объявлен вперед.