在头文件中使用类而无法访问其定义?

时间:2010-12-16 16:39:35

标签: c++ implicit-conversion google-style-guide

摘自google's c++ coding guidelines

  

我们如何在标题中使用类Foo   文件无法访问其定义?

     
      
  • 我们可以声明Foo *或Foo&类型的数据成员。
  •   
  • 我们可以使用参数声明(但不定义)函数,和/或   返回值,类型为Foo。 (一   异常是如果参数Foo或   const Foo&有一个非显式的,   单参数构造函数,其中   我们需要完整的定义   支持自动类型转换。)
  •   
  • 我们可以声明Foo类型的静态数据成员。这是因为静态   数据成员在外部定义   课程定义。
  •   

我很好奇的是第二个子弹中的异常。为什么会这样?如果我们想支持自动类型转换,为什么需要完整定义?

我的猜测是编译器需要目标类型的完整定义,因为隐式转换中创建了临时对象。我猜对了吗?还有更多吗?

编辑:

正如我所看到的,指南中的例外情况是针对这样的情况:

class A
{
    public:
        A( int );
};

class B
{
    public:
        B( A const &a );
};

int main()
{
    B b(2);
}

这里我们只有一个用户定义的隐式转换(从int到A),并调用接受A const&的构造函数。只有在这个例外中有意义的是支持从例如直接转换。 int到A,然后通过接受A const&的构造函数到B,允许客户端代码使用这个转换链,而不需要显式地包含声明A类的头文件。

3 个答案:

答案 0 :(得分:2)

C ++语言不区分头文件中的代码和其他文件。它甚至不要求标题是文件。从技术上讲,这个问题毫无意义,但实际上你会限制你在头文件中所做的事情,以免违反一个定义规则。在不限制自己的情况下,用户必须小心,只将头文件包含在一个翻译单元中。通过适当的限制,头文件可以自由地包含在多个翻译单元中。

不完整类型是指大小未知的地方,其中sizeof无法使用。

当类定义未知时,类Foo必然是不完整的。

这意味着你无法做出需要知道大小的事情。而且由于不完整意味着成员不为人所知(如果知道他们的大小就必须知道),你通常不能召集任何成员。例外:可以调用析构函数,就像在delete pFoo中一样,并且编译器必须接受它,但如果类Foo具有非平凡的析构函数,则它是未定义的行为。

然而,Google指南中提到的例外情况毫无意义。

编辑:我发现,当事情详细阐述时,SO上的人会更喜欢,所以,添加为什么指南的讨论毫无意义。

指南说你可以“声明(但不定义)”但是“如果参数Foo或const Foo&具有非显式的单参数构造函数,则会出现一个例外”。

声明与构造函数没有任何关系,可以通过简单的尝试来确认:

#include <iostream>

struct Foo;

Foo bar( Foo const& );  // Declaration of function bar, works fine.

struct Foo
{
    int x_;
    Foo( int x ): x_( x ) {}       // Converting constructor.
};

int main()
{
    std::cout << bar( 42 ).x_ << std::endl;
}

Foo bar( Foo const& foo ) { return foo; }

总之,Google指南的例外情况再次毫无意义。

干杯&amp;第h。,

答案 1 :(得分:0)

假设foo.h只知道Foo声明

//foo.h

class Foo;
void f(const Foo &); // It is possible to use the reference.

完整定义位于foo.cpp

// foo.cpp

class CanBeConvertedToFoo;
class Foo
{
   Foo (const CanBeConvertedToFoo & x); // implicit constructor
}

class CanBeConvertedToFoo隐式可转换为Foo; 但在some.cpp中未知。

// some.cpp

#include "foo.h"
void g(const CanBeConvertedToFoo & x) {
   f(x); // Is it known about implicit conversion ?
}

答案 2 :(得分:0)

我不知道第二点的例外是否属实。隐式转换只有在调用函数时才能知道,而不是在声明函数时才知道,所以即使在C声明f#include <iostream> class C; void f(C); struct C { C(int i) { std::cout << "C(" << i << ")" << std::endl; } }; void f(C c) { std::cout << "f(C)" << std::endl; } int main() { f(2); } 不完整,以下情况仍然有效:

{{1}}