为什么 C 结构体黑科技在 C++ 模板声明中不起作用?

4

C语言有一个很有趣的技巧,一个名称可以用来声明类型和函数:

struct foo {};
void foo(void);

现在,每当我写foo时,编译器会默认我指的是函数,除非我在前面加上struct前缀:
foo();  // call void foo(void)
struct foo bar;  // declare variable of type struct foo

这在C++中非常好用,甚至可以处理命名空间和嵌套!但是它无法与模板一起使用:

template <typename> void bar(void);
template <typename> struct bar {};
// error: conflicting declaration of template struct bar
// previous declaration: template void bar()

为什么?

我们已经在依赖上下文中处理歧义消解器:

typename foo<A>::b();
foo<A>::template b<C>();

将常规规则应用于这种模板名称的歧义似乎并不困难。

假设对我而言拥有相同的名称很重要,我能否以某种方式使其起作用?


2
如果您正在寻找C++标准的“章节和诗句”,或许您需要添加language-lawyer标签,并且可能需要交叉引用C标准以获取细微差别和相似之处。 - Eljay
好的建议。已完成。 - Filipp
因为这是struct hack,而不是template hack。 - Pete Becker
2个回答

4

为什么?

答案在问题的主体中。这适用于类和函数,因为在C语言中,结构标签和函数标识符有不同的命名空间。如果C++旨在与C代码互操作,它必须保留此行为。只需要看一下像stat函数这样的API,它需要一个struct stat参数,就可以意识到为什么C++保留了这种代码的有效性。这个命名空间的工作方式与C代码驻留在全局命名空间中是一致的(所以当需要时,::statstruct ::stat应该继续工作)。

然而,在C++中,类标记的标识符与常规标识符共享一个“命名空间”。因此,这个“hack”的有效性是通过C++规范中的一个特殊情况来实现的,它简单地隐藏了当它们发生冲突时的类。

[basic.scope.hiding]

2 在同一作用域内,类名或枚举名可能会被变量、数据成员、函数或枚举器的名称隐藏。如果在同一作用域(以任何顺序)中声明了一个类或枚举名和一个变量、数据成员、函数或枚举器具有相同的名称,则无论变量、数据成员、函数或枚举器名称在哪里可见,该类或枚举名都会被隐藏。

不管C++的设计者认为它是好事还是坏事都没关系。有一些遗留代码需要继续被C++编译器接受,以便与某些系统进行交互。

但是,没有涉及到需要相同行为的模板的旧代码。模板完全是C++构造的(与C无关)。因此,语言设计没有外部约束来增加模板名称的更多特殊情况。因此,也没有这样做。

假设对我来说拥有相同的名称很重要。我能想办法实现这个吗?

没有想到让它工作的方法。


3
这段话涉及编程相关内容,讲述了类模板命名的规则。在相同作用域内,类模板不能与其他模板、类、函数、变量、枚举类型、命名空间或类型同名。除非在[temp.class.spec]中另有规定。temp.pre-7temp.class.spec提到的内容并不矛盾。因此,在当前情况下,你不能将类模板和函数模板命名为相同的名称。

委员会是否考虑取消这个限制作为实施CTAD的一种方式? - Filipp
我不确定你的意思;我们已经有了CTAD。而且,我认为这不是一种限制,而是一种特性。理想情况下,即使对于非模板,我也希望不能这样做。 - cigien
今天我们有CTAD,但在14点到17点之间这是一个悬而未决的问题。std::pair a(1, 2);auto a = std::pair(1, 2);好多少?后者使用类似于std::make_pair的函数。 - Filipp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接