【问题标题】:Why is this a forward declaration in C++?为什么这是 C++ 中的前向声明?
【发布时间】:2017-03-08 12:12:44
【问题描述】:

我将在utilA.cpp中有如下代码sn-p:

// 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
     }
}

编译utilA.cpp时GCC报错,

error: variable 'xm::tm time1' has initializer but incomplete type

这似乎是因为utilA.h在第0行使用struct tm,但没有包含time.h,编译器将第0行的struct tm视为前向声明,所以struct tm在第 2 行被解析为 xm::tm 在第 0 行的标题内。

那么 C++ 标准是否将 struct tm 定义为一种函数参数类型作为前向声明?请帮助解释这一点,标准中的引用会有所帮助。

【问题讨论】:

  • 在标题中写入#include &lt;time.h&gt;,因为您的标题依赖于它。
  • 是的,我们应该在utilB.h中添加#include ,但是因为这是来自一个巨大项目的代码sn-p,在我们找到根本原因之前它让我们很困惑。
  • 这是一种非常糟糕的编程风格。忽略它。 PS:有没有试过把#include-s 移到顶部?
  • @molbdnilo 他们不需要tm 的定义来声明zoo。除非你的意思是说你不能前向声明标准库类型,在这种情况下你是对的
  • @krzaq 不,但每次使用zoo 都需要定义。拥有独立的标题是一种很好的形式。

标签: c++ language-lawyer standards forward-declaration


【解决方案1】:

在第 0 行,您在 xm 命名空间内声明了一个名为 tm 的类。是的,C++ 允许在函数/模板参数中声明类型。

N4140 § 3.4.4 [basic.lookup.elab]/2

如果 elaborated-type-specifierclass-key 引入并且 此查找未找到先前声明的 type-name,或者如果 elaborated-type-specifier 以如下形式出现在声明中:

class-key attribute-specifier-seqopt标识符;

elaborated-type-specifier 是一个声明,它引入了 3.3.2 中描述的类名。

因为您在 xm 命名空间内声明了一个名为 tm 的类,所以它是名称查找在第 3 行中为 tm 找到的第一个名称。不考虑 ::tm(和 ::std::tm)。由于没有定义类::xm::tm,编译器会抱怨它是一个不完整的类型。

如果你不是用 C++ 编写 C 代码,你会写类似1

struct tm;

namespace xz{
    void zoo(tm timeval);
}

#include <ctime>

namespace xz{
    void zoo(tm timeval);
}

你不会有这个问题。

1请记住,您不能在命名空间 std 中前向声明名称

【讨论】:

    【解决方案2】:

    C++ 标准也将此struct tm 定义为一种函数参数类型作为前向声明。请帮助解释这一点,标准中的配额会有所帮助。

    是的,struct tm timeval 将在这里引入一个新的类名xm::tm


    (解释和引用)

    struct tm 是一个elaborated type specifier,可以用来引入一个新的类名。

    $3.1/4 Declarations and definitions [basic.def]

    [ 注意:类名也可以通过详细类型说明符 ([dcl.type.elab]) 隐式声明。 ——尾注]

    $9.1/2 Class names [class.name]:

    仅由类键标识符组成的声明;要么是 在当前范围内重新声明名称或转发 将标识符声明为类名。它介绍了类 名称到当前范围内。

    $3.4.4/2 Elaborated type specifiers [basic.lookup.elab]:

    或者如果详细类型说明符出现在声明中 形式:

    class-key attribute-specifier-seqopt identifier ; 
    

    详细类型说明符是一个声明,它引入了 [basic.scope.pdecl] 中描述的类名。

    $3.3.2/7 Point of declaration [basic.scope.pdecl]:

    如果在 decl-specifier-seq 中使用了详细类型说明符,或者 在命名空间范围内定义的函数的参数声明子句, 标识符被声明为命名空间中的类名 包含声明;

    对于struct tm timeval用作函数参数声明,因为&lt;time.h&gt;不包括在内,仍然没有名为tm的类,类tm将在当前范围内声明(即命名空间xm),然后@ 987654336@ 将被转发声明。

    【讨论】:

      猜你喜欢
      • 2011-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多