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

发布于 2024-10-07 23:29:20 字数 941 浏览 4 评论 0原文

此内容摘自 Google 的 C++ 编码指南

我们如何在标头中使用 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 类所在的头文件被宣布。

This is excerpt from google's c++ coding guidelines.

How can we use a class Foo in a header
file without access to its definition?

  • We can declare data members of type Foo* or Foo&.
  • We can declare (but not define) functions with arguments, and/or
    return values, of type Foo. (One
    exception is if an argument Foo or
    const Foo& has a non-explicit,
    one-argument constructor, in which
    case we need the full definition to
    support automatic type conversion.)
  • We can declare static data members of type Foo. This is because static
    data members are defined outside the
    class definition.

What I'm curious about is exception in the second bullet. Why is this so? Why is the full definition needed if we want to support automatic type conversion?

My guess is that compiler needs the full definition of the destination type because of the temporary object that is created in the implicit conversion. Am I guessing correctly? Is there more to it?

EDIT:

As I see it, the exception in the guideline is addressed to situation like this:

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

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

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

Here we have only one user-defined implicit conversion (from int to A), and call to constructor that accepts A const &. Only thing that makes sense in this exception is to support direct conversion from e.g. int to A, and then to B via constructor that accepts A const &, allowing client code to use this conversion chain without need to explicitly include header file where A class is declared.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

泪眸﹌ 2024-10-14 23:29:20

C++ 语言不区分头文件和其他文件中的代码。它甚至不要求标头是文件。因此,纯粹从技术上讲,这个问题是没有意义的,但实际上,您可以限制在头文件中执行的操作,以免违反“单一定义规则”。在不限制自己的情况下,用户必须小心地只将头文件包含在一个翻译单元中。通过适当的限制,头文件可以自由地包含在多个翻译单元中。

不完整类型是一种大小未知的类型,无法使用sizeof

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

这意味着您无法执行需要知道大小的操作。由于不完整性意味着成员未知(如果大小已知,则它们必然是已知的),因此您通常不能调用任何成员。例外:您可以调用析构函数,就像在delete pFoo中一样,编译器必须接受它,但如果类Foo有一个非平凡的析构函数。

然而,谷歌指南中指出的例外是没有意义的。

编辑:我发现 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 指南的例外是没有意义的。

干杯&呵呵,

The C++ language doesn't differentiate between code in header files and other file. It does not even require that a header is a file. So purely technically the question is meaningless, but in practice you restrict what you do in header files so as not to run afoul of the One Definition Rule. Without restricting yourself, users would have to be careful to only include the header file in one translation unit. With proper restrictions, the header file can be freely included in multiple translation units.

An incomplete type is one where the size is not known, where sizeof cannot be used.

When the class definition is not known, class Foo is necessarily incomplete.

This means you cannot do things that requires the size to be known. And since incompleteness means that members are not known (they would necessarily be known if the size was known) you can't generally call any members. Exception: you can call the destructor, like in delete pFoo, and the compiler must accept that, but it's Undefined Behavior if class Foo has a non-trivial destructor.

The exception noted in the Google guidelines is, however, meaningless.

EDIT: I discovered that people on SO like it better when things are spelled out in detail, so, adding discussion of why the guideline is meaningless.

The guideline says you can "declare (but not define)" but that "one exception is if an argument Foo or const Foo& has a non-explicit, one-argument constructor".

The declaration does not have anything to do with constructors, which one can affirm by simply trying it out:

#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; }

In conclusion, again, the Google guidelines' exception is meaningless.

Cheers & hth.,

放我走吧 2024-10-14 23:29:20

假设 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 ?
}

Suppose that foo.h knows about Foo declaration only

//foo.h

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

Full definition is in foo.cpp

// foo.cpp

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

class CanBeConvertedToFoo is implicit convertable to Foo;
But it is unknown in some.cpp.

// some.cpp

#include "foo.h"
void g(const CanBeConvertedToFoo & x) {
   f(x); // Is it known about implicit conversion ?
}
小鸟爱天空丶 2024-10-14 23:29:20

我不知道第二点的例外是否属实。隐式转换必须仅在调用函数时才知道,而不是在声明函数时才知道,因此即使在声明 fC 不完整,以下操作仍然有效:

#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); }

I don't know whether the exception in the second point is true. Implicit conversions must be know only when a function is called, not when it is declared, so the following works even though C is incomplete while f is declared:

#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); }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文