gpt4 book ai didi

c++ - 当用作模板参数时,C++编译器不区分类型和函数名称

转载 作者:行者123 更新时间:2023-11-30 01:25:38 24 4
gpt4 key购买 nike

我已经使用g++clang++尝试了以下代码。两者都无法区分foo主体中的foo类型和函数名称foo。为什么会这样呢? C++标准是否要求这样做?编译器是否应该至少同时尝试两者?

enum foo {
FOO = 0,
BAR,
BAZ
};

class Bar {
public:

foo foo () const
{
// does not compile if I write static_cast<foo>(...)
return static_cast< ::foo>(m_bar);
}

int m_bar;
};

int main ()
{
Bar bar;
bar.m_bar = 0;
foo foo_bar = bar.foo();
return 0;
}

我可以将 ::foo替换为 enum foo,它将编译得很好。但是,如果我将 enum foo {...}更改为 typedef enum _foo {...} foo,则仍然存在相同的问题( http://ideone.com/d1GiO)。

最佳答案

在C和C++(继承)中,有两个单独的标识符空间:一个用于通用符号,包括变量,函数...,另一个用于用户定义类型(枚举,结构)。 typedef表达式在通用标识符空间内为用户定义的类型创建别名。

因为标识符空间是分开的,所以即使在相同的上下文中,该语言也允许您定义具有相同名称的函数和类型:

enum foo { no, yes };
void foo() {}

在C语言中,您被迫明确地在用户定义的类型空间中限定标识符,因此在上一个示例中,要使用 foo类型,您必须执行 enum foo:
void foo( enum foo ) {} 

现在, typedef在全局标识符空间中创建了一个别名,因此,使用typedef可以无限制地逃脱:
typedef enum bar { no, yes } bar;
void foo( bar ) {} // uses the typedef

随着时间的推移,使用C++ [*],两个标识符空间仍在该语言中,已改变的是查找规则。在全局标识符空间中查找标识符时(即,没有前面的 enumstructclass关键字,并且在允许使用非类型的上下文中),编译器将从最内部的范围开始搜索到外部的范围,并且对于每个作用域,它将首先在全局标识符空间中查找,如果在那里找不到任何内容,则还将在用户定义的类型中查找。这就是 enumstructclass可选的原因。在大多数情况下,就是这样。

在您的特定情况下,您正在做的一些事情可能会使结果令人惊讶。首先,您有一个声明,该声明使用相同的标识符来引用两个不同的事物。这很好,因为在到达函数名称之前,唯一的 foo实体是 enum:
foo         // No need for 'enum', at this point function 'foo' is not declared
foo(); // No collision, this is 'foo' in global id space, not 'enum foo'

现在,在 Bar::foo内部,如果单独使用 foo,它将开始在函数范围内查找,在此范围内什么也找不到。然后它将移至 Bar类范围,在该范围中将看到存在 foo函数,并且查找将在那里停止。请注意,是否为枚举器使用typedef都没有任何区别,因为在离开 Bar类之前(因此可能在 namespace 级别找到任何一个标识符之前),查找将停止。

如您所述,如果添加 enum关键字,那么突然您会更改规则。现在,您正在寻找用户定义的类型,并且搜索将遵循相同的范围,但只会在标识符空间中查找用户定义的类型。因此它将从 Bar::foo变为 Bar,那里没有 enum foo,因此它将向外继续直到找到命名空间级别的枚举。

如果提供 namespace 限定,则会发生相同的事情:在 foo之前加上 ::要求查找从全局 namespace 级别开始。在该级别,唯一定义的 foo是用户定义的 enum foo类型。请注意,这与添加或不添加 enum关键字正交。在原始代码中,您可能在 namespace 级别具有 foo函数,这将导致类似的问题:
enum foo { no, yes };
void foo() {}
class Bar {
foo foo() { // Error [1]
return static_cast<::foo>(0); // Error [2]
}
};

在此示例中,有两个错误。声明返回类型时,尚未声明函数 Bar::foo,但是存在 ::foo(),按照上述规则,查找将向外进行,直到全局命名空间并在检查 ::foo()之前找到 enum foo。第二个错误,基本上是相同的,是 static_cast中要求全局命名空间的限定在这里无济于事。原因是查找将在找到 ::foo()之前再次找到 enum foo。正确的代码是:
class Bar {
enum foo foo() {
return static_cast<enum ::foo>(0); // :: is optional!
}
};

在这种情况下, namespace 限定还是可选的,但这仅是因为没有可通过查找找到的 foo用户定义类型。但是我们可以使代码更加复杂...
enum foo { no, yes };
void foo() {}
class Bar {
struct foo {};
enum ::foo foo() {
return static_cast<enum ::foo>(0);
}
};

如果没有 ::资格,则 Bar::foo()声明和 static_cast都将失败,因为存在用户定义的 Bar::foo类型,将在找到 enum ::foo之前将其击中。没有 enum,代码也将无法编译,因为全局函数 ::foo()将在 enum之前考虑。

总结一下,并经过很长的解释: 不要。避免创建具有相同类型名称的函数,因为这很可能会引起困惑。

[*]重新阅读标准后,C++语言未为用户定义的类型定义单独的标识符空间。另一方面,该标准中的规则与上述描述一致。主要区别在于,在C++中, typedef别名除了要作为别名的类型之外,不能与任何现有类型相同:
struct foo {};
typedef int foo; // Error

但是对于大多数其他目的,行为是一致的。我在答案正文中没有提到几个极端的情况,但是这超出了此问题的范围。

关于c++ - 当用作模板参数时,C++编译器不区分类型和函数名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12044667/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com