每个人都同意
using <typedef-name> = <type>;
相当于
typedef <type> <typedef-name>;
并且前者因各种原因而优先于后者(参见Scott Meyers,Effective Modern C ++以及有关stackoverflow的各种相关问题)。
这由[dcl.typedef]支持:
也可以通过别名声明引入typedef-name。 using关键字后面的标识符 成为一个typedef-name和可选的attribute-specifier-seq,后面跟着标识符 typedef的名称。这样的typedef-name具有与typedef说明符引入的语义相同的语义。
但是,请考虑声明,例如
typedef struct {
int val;
} A;
对于这种情况,[dcl.typedef]指定:
如果typedef声明定义了一个未命名的类(或枚举),那么第一个声明的typedef-name 声明为类类型(或枚举类型)用于表示链接的类类型(或枚举类型) 仅限用途(3.5)。
参考部分3.5 [basic.link]说
具有未命名的命名空间范围的名称 上面给出的内部链接与封闭命名空间具有相同的链接,如果它是名称 [...] 在类具有的typedef声明中定义的未命名类 用于链接目的的typedef名称[...]
假设上面的typedef声明是在全局命名空间中完成的,那么struct A
将具有外部链接,因为全局命名空间具有外部链接。
现在的问题是,如果将typedef声明替换为别名声明,根据它们是等效的常见概念,是否也是如此:
using A = struct {
int val;
};
特别是,通过别名声明(“using”)声明的类型A
与通过typedef声明声明的类型具有相同的链接吗?
注意[decl.typedef]并没有说别名声明是 typedef声明(它只说两者都引入了typedef-name)而[decl.typedef]只说了一个 typedef声明(不是别名声明),具有为链接目的引入 typedef名称的属性。
如果别名声明无法为链接目的引入typedef名称,A
将只是匿名类型的别名,并且根本没有链接。
这引发了后续问题:
以下由三个文件组成的最小程序(我们至少需要两个独立的编译单元)用于调查此问题。
#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
#include "a.hpp"
#include <iostream>
void print(std::ostream& os, A const& a)
{
os << a.val << "\n";
}
#include "a.hpp"
#include <iostream>
int main()
{
A a;
a.val = 42;
print(std::cout, a);
}
使用gcc 7.2对“typedef”变体进行编译,干净地编译并提供预期的输出:
> g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
> ./a.out
42
使用“using”变体进行编译会产生编译错误:
> 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和别名声明之间的联系产生了影响。
使用clang 6,两种变体都可以编译和运行,没有任何警告:
> 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
因此也可以问
答案 0 :(得分:10)
这对我来说就像GCC中的一个错误。
请注意,[decl.typedef]并未说别名声明是typedef声明
你是对的,[dcl.dcl] p9给出了术语 typedef声明的定义,它排除了 alias-declaration 。但是,正如您在问题中引用的那样,[dcl.typedef]明确地说:
2 alde-name 也可以通过 alias-declaration 引入。
using
关键字后面的标识符变为 typedef-name ,标识符后面的可选属性说明符-seq 附属于 typedef-name 。 它具有与typedef
说明符引入的语义相同的语义。 [...]
&#34;相同的语义&#34;没有任何疑问。根据海湾合作委员会的解释,typedef
和using
具有不同的语义,因此唯一合理的结论是海湾合作委员会的解释是错误的。应用于typedef声明的任何规则也必须解释为应用于别名声明。
答案 1 :(得分:3)
看起来这个标准还不清楚。
一方面,
[dcl.typedef] alde-name 也可以通过 alias-declaration 引入。 [...]这样的 typedef-name 具有与
typedef
说明符引入的语义相同的语义。
另一方面,该标准明确区分了typedef声明和 alias-declaration 的概念(后一术语是语法生成名称,所以它是斜体和连字符;前者不是) 。在某些情况下,它会讨论&#34; typedef声明或 alias-declaration &#34;,使它们在这些上下文中等效;有时它只谈及&#34;一个typedef声明&#34;。特别是,每当标准讨论链接和typedef声明时,它只讨论typedef声明,而不提及 alias-declaration 。这包括关键段落
[dcl.typedef] 如果typedef声明定义了一个未命名的类(或枚举),则声明声明的第一个 typedef-name 是该类的类型(或枚举类型)用于表示链接的类类型(或枚举类型) 仅用途。
请注意,标准坚持使用第一个 typedef-name 进行链接。这意味着在
typedef struct { int x; } A, B;
仅A
用于链接,B
不用。标准中没有任何内容表明 alias-declaration 引入的名称应该像A
一样,而不是B
。
我认为该标准在该领域尚不明确。如果目的是仅使typedef声明用于链接,那么在[dcl.typedef]中明确声明 alias-declaration 不会。如果目的是使 alias-declaration 用于链接,则应该明确说明,就像在其他上下文中一样。