使用vs. typedef-有细微的,鲜为人知的区别吗? [英] using vs. typedef - is there a subtle, lesser known difference?

查看:173
本文介绍了使用vs. typedef-有细微的,鲜为人知的区别吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每个人都同意

using <typedef-name> = <type>;

等同于

typedef <type> <typedef-name>;

并且出于各种原因,前者应优先于后者(请参阅Scott Meyers,Effective Modern C ++和有关stackoverflow的各种相关问题).

and that the former is to be preferred to the latter for various reasons (see Scott Meyers, Effective Modern C++ and various related questions on stackoverflow).

这由[dcl.typedef]支持:

This is backed by [dcl.typedef]:

也可以通过别名声明引入typedef名称. using关键字后面的标识符 成为typedef名称,并且标识符后面的可选的attribute-specifier-seq属于该名称 typedef名称.这样的typedef名称与typedef说明符引入的语义相同.

A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. Such a typedef-name has the same semantics as if it were introduced by the typedef specifier.

但是,考虑一个声明,例如

However, consider a declaration such as

typedef struct {
    int val;
} A;

在这种情况下,[dcl.typedef]指定:

For this case, [dcl.typedef] specifies:

如果typedef声明定义了一个未命名的类(或枚举),则由 声明为类类型(或枚举类型)用于表示链接的类类型(或枚举类型) 仅出于目的(3.5).

If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only (3.5).

引用的第3.5节[basic.link]说

The referenced section 3.5 [basic.link] says

具有名称空间范围但没有名称空间范围的名称 如果上面的内部链接是的名称,则内部链接与封闭的名称空间具有相同的链接 [...] 在typedef声明中定义的未命名类,其中该类具有 用于链接目的的typedef名称[...]

A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of [...] an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes [...]

假设上面的typedef声明是在全局名称空间中完成的,则结构A将具有外部链接,因为全局名称空间具有外部链接.

Assuming the typedef declaration above is done in the global namespace, the struct A would have external linkage, since the global namespace has external linkage.

现在的问题是,如果根据相同的通用概念将typedef声明替换为别名声明,是否同样成立:

The question is now whether the same is true, if the typedef declaration is replaced by an alias declaration according to the common notion that they are equivalent:

using A = struct {
    int val;
};

尤其是,通过别名声明("using")声明的类型A是否与通过typedef声明声明的类型具有相同的链接?

In particular, does the type A declared via the alias declaration ("using") have the same linkage as the one declared via the typedef declaration?

请注意,[decl.typedef]并不表示别名声明 typedef声明(它只说这两者都引入了typedef名称),而[decl.typedef]仅表示具有为链接目的引入 typedef名称的属性的 typedef声明(不是别名声明). 如果别名声明不能为链接目的引入typedef名称,则A只是匿名类型的别名,根本没有链接.

Note that [decl.typedef] does not say that an alias declaration is a typedef declaration (it only says that both introduce a typedef-name) and that [decl.typedef] speaks only of a typedef declaration (not an alias declaration) having the property of introducing a typedef name for linkage purposes. If the alias declaration is not capable of introducing a typedef name for linkage purposes, A would just be an alias for an anonymous type and have no linkage at all.

IMO,尽管严格,但这至少是对标准的一种解释.当然,我可能会忽略某些东西.

IMO, that's at least one possible, albeit strict, interpretation of the standard. Of course, I may be overlooking something.

这引起了随后的问题:

  • 如果确实存在这种细微的差异,是出于故意还是出于 是对标准的疏忽吗?
  • 编译器/链接器的预期行为是什么?
  • If there is indeed this subtle difference, is it by intention or is it an oversight in the standard?
  • What is the expected behavior of compilers/linkers?

下面的由三个文件组成的最小程序(我们至少需要两个单独的编译单元)用于调查问题.

The following minimal program consisting of three files (we need at least two separate compilation units) is used to investigate the issue.

#ifndef A_HPP
#define A_HPP

#include <iosfwd>

#if USING_VS_TYPEDEF
using A = struct {
     int val;
};
#else
typedef struct {
     int val;
} A;
#endif

void print(std::ostream& os, A const& a);

#endif // A_HPP

a.cpp

#include "a.hpp"
#include <iostream>

void print(std::ostream& os, A const& a)
{
   os << a.val << "\n";
}

main.cpp

#include "a.hpp"
#include <iostream>

int main()
{
    A a;
    a.val = 42;
    print(std::cout, a);
}

海湾合作委员会

使用带有"typedef"变体的gcc 7.2对此进行编译,可以干净地编译并提供预期的输出:

GCC

Compiling this with gcc 7.2 with the "typedef" variant compiles cleanly and provides the expected output:

> g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
> ./a.out
42

带有"using"变体的编译会产生编译错误:

The compilation with the "using" variant produces a compile error:

> g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
a.cpp:4:6: warning: ‘void print(std::ostream&, const A&)’ defined but not used [-Wunused-function]
void print(std::ostream& os, A const& a)
     ^~~~~
In file included from main.cpp:1:0:
a.hpp:16:6: error: ‘void print(std::ostream&, const A&)’, declared using unnamed type, is used but never defined [-fpermissive]
void print(std::ostream& os, A const& a);
     ^~~~~
a.hpp:9:2: note: ‘using A = struct<unnamed>’ does not refer to the unqualified type, so it is not used for linkage
};
 ^
a.hpp:16:6: error: ‘void print(std::ostream&, const A&)’ used but never defined
void print(std::ostream& os, A const& a);
     ^~~~~

看起来GCC遵循上述标准的严格解释,并且在typedef和别名声明之间的链接方面有所不同.

This looks like GCC follows the strict interpretation of the standard above and makes a difference concerning linkage between the typedef and the alias declaration.

使用clang 6,两个变体均可编译并干净运行,而不会发出任何警告:

Using clang 6, both variants compile and run cleanly without any warnings:

> clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
> ./a.out
42

> clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
> ./a.out
42

因此,一个人也可能会问

One could therefore also ask

  • 哪个编译器正确?

推荐答案

在我看来,这就像是GCC中的错误.

This looks to me like a bug in GCC.

请注意,[decl.typedef]并不表示别名声明是typedef声明

Note that [decl.typedef] does not say that an alias declaration is a typedef declaration

是的,[dcl.dcl] p9给出了 typedef声明的定义,其中不包括 alias-declaration .但是,正如您在问题中引用的那样,[dcl.typedef]确实明确地说过:

You're right, [dcl.dcl]p9 gives a definition of the term typedef declaration which excludes alias-declarations. However, [dcl.typedef] does explicitly say, as you quoted in your question:

2 typedef-name 也可以通过 alias-declaration 引入.紧随using关键字的 identifier 变为 typedef-name ,而紧随 identifier 的可选 attribute-specifier-seq em>属于该 typedef-name . 它的语义与typedef说明符引入的语义相同. [...]

2 A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. [...]

相同的语义"毫无疑问.在GCC的解释下,typedefusing具有不同的语义,因此唯一合理的结论是GCC的解释是错误的.任何适用于typedef声明的规则也必须解释为也适用于别名声明.

"The same semantics" doesn't leave any doubt. Under GCC's interpretation, typedef and using have different semantics, therefore the only reasonable conclusion is that GCC's interpretation is wrong. Any rules applying to typedef declarations must be interpreted as applying to alias-declarations as well.

这篇关于使用vs. typedef-有细微的,鲜为人知的区别吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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