为什么在函数声明中允许前向声明? [英] Why is a forward declaration in a function declaration allowed?

查看:114
本文介绍了为什么在函数声明中允许前向声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在阅读有关访客模式的内容时,我遇到了以下代码片段:

While reading about the visitor pattern I ran into this snippet of code:

virtual void visit(class Composite *, Component*) = 0;

这是一个成员函数,似乎正在向前声明该类参数内的Composite 。我只是用一个普通的函数来尝试过这个,就像这样:

This is a member function, and it seems to be forward declaring the class Composite inside its parameters. I tried this with just a normal function, like so:

void accept(class A a);

对于某些我没有的课程 A 尚未声明或定义,并且代码运行正常。为什么允许这样做? (如果有的话)与在行前进行前向声明有什么不同?

for some class A that I haven't declared or defined yet and the code worked fine. Why is this allowed? How, if at all, is it different from forward declaring before the line? Has anything changed recently in the standard in regards to this?

许多人声称这是C的剩余物,但是最近对此有什么改变吗?那么为什么该代码在C ++中可以正常编译,但在C语言中不能正常编译?

Many people are claiming this is a leftover of C, but then why does this code compile fine in C++, but not C?

#include <stdio.h>
int process(struct A a);

struct A{
    int x;
};

int process(struct A a){
    return a.x;
}

int main(void)
{
    struct A a = {2};
    printf("%d", process(a));
    return 0;
}


推荐答案

这称为不完整类型,并且是从C继承的C ++概念。

This is called an incomplete type, and is a concept C++ inherited from C.

不完整类型的工作方式是:在定义之前类B 在您的代码中,您可以将类B变量名用作函数原型中的参数,或将此类型的指针用作 class B * ptr -真正不需要类型名称的任何地方。

Incomplete types work this way: before you've defined a class B in your code, you can use class B varname as, say, an argument in function prototypes, or use pointers to this type as class B* ptr - anywhere where no details about a type besides its name are really needed.

实际上,您可以用不同的方式书写-只需将 class B; (应该作为类 declaration 使用)放在不完整的类型之前,然后您可以编写 B varname 代替 class B varname

Actually, you can write it differently - just put a class B; (which should work as a class declaration) before you use it as an incomplete type, and then you can write B varname instead of class B varname.

指向不完整类型的指针通常与 不透明指针 一起使用,这可能是C ++中不完整类型的最常见用法。链接的Wikipedia文章中对不透明指针的描述足够好。简而言之,它是一种允许您的API隐藏整个类实现的技术。

Pointers to incomplete types are often used with opaque pointers, which are probably the most common use of incomplete types in C++. Opaque pointers are described well enough in the linked Wikipedia article. Put short, it is a technique that allows your API to hide an entire class implementation.

在问题中描述的是不完整的类型语法,该示例来自Wikipedia:

Using the incomplete type syntaxis you describe in your question, the example code from Wikipedia:

//header file:
class Handle {
public:
    /* ... */

private:
    struct CheshireCat;        // Not defined here
    CheshireCat* smile;        // Handle
};

//CPP file:

struct Handle::CheshireCat {
    int a;
    int b;
};

可以重写为:

//header file:
class Handle {
public:
    /* ... */

private:
    struct CheshireCat* smile;        // Handle
};

//CPP file:

struct CheshireCat {
    int a;
    int b;
};

请注意:这些代码段与等价,如前者所定义类型为 Handle :: CheshireCat ,而后者的简称为 CheshireCat

Note this: these code snippets are not equivalent, as the former defines a type Handle::CheshireCat, while the latter has it simply as CheshireCat.

在您作为示例提供的代码上

在C语言中,未编译的原因非常明显很简单:函数原型中的 struct A 是一个作用于函数原型的声明,因此它与 struct A 声明为后者。对于这种特定情况,C和C ++的规则略有不同。如果您像这样向前声明 struct struct A; 在函数原型之前,它将以两种语言编译!

In C, the reason it doesn't compile is quite simple: the struct A in the function prototype is a declaration scoped to the function prototype, and thus it is different from the struct A which is declared latter. C and C++ have slightly different rules for this specific case. If you forward-declare the struct like this: struct A; before the function prototype, it will compile in both languages!

此语法的其他显着用法是:

此语法具有重要意义作为C ++与C的向后兼容性的一部分。您会在C中看到,在定义或向前声明 struct 之后,就像这样: struct A {}; struct A; ,该类型的实际名称为 struct A 。要将名称用作 A ,您需要使用 typedef 。 C ++自动执行后者,但是允许您同时将 A 用作 struct A A class -es union -s和 enum -s。

This syntaxis has an important place as part of C++'s backward compatibility with C. You see, in C, after defining or forward-declaring a struct like this: struct A {}; or struct A;, the type's actual name would be struct A. To use the name as A, you needed to use a typedef. C++ does the latter automatically, but allows you to use A both as struct A and A. Same goes for class-es union-s, and enum-s.

实际上,有人认为这在语义上很重要。考虑具有以下签名的函数: int asdf(A * paramname)。您只看声明就知道 A 是什么吗?是结构枚举还是工会?人们说可以通过以下方式使这样的签名更清晰: int asdf(enum A * paramname)。这是一种编写自文档代码的好方法。

Actually, some argue this has a semantical importance. Consider a function with the following signature: int asdf(A *paramname). Do you know what A is just by looking at the declaration? Is it a class, struct, enum or a union? People say that a signature like that can be made clearer in such a way: int asdf(enum A *paramname). This is a nice way of writing self-documenting code.

这篇关于为什么在函数声明中允许前向声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆