c++ - 奇怪的 C++ namespace 解析怪癖和 g++ 与 clang++

标签 c++ c++11 language-lawyer

<分区>

这始于观察。我更改了一些看起来有点像这样的代码(编辑:我在这里取出了指定的初始化器,它们也不在原始代码中):

struct S {
    enum E { E1, E2 } member;
}

// file1.cc
S v1 = { S::E1 };

// file2.cc
S v2 = { S::S::E2 };

请注意 file2.cc 过度限定了 E2。然而,这在 g++ 和 clang++ 中都有效。 (编辑 2:这个特定 VM 上的 g++ 是 g++-5.4.1,但原始代码已经通过早期和后来的 g++ 版本,加上多个 clang 版本。)事实上,我们可以写:

S v3 = { S::S::S::S::S::S::S::E1 };

(无论我们喜欢多少 S::),无论我们喜欢什么。我改变了一些东西,使 S 不再是一个普通的 struct,而是一个模板化的结构,之后它就停止工作了。没什么大不了的,但这让我很好奇,所以我进行了试验。

如果我们将其更改为非 POD 类型:

struct S {
    S() { std::cout << "made an S" << std::endl; }
    enum E { E1, E2 } member;
}

(使用适当的#include)它不再被允许。 Clang 和 g++ 产生不同的诊断。这是 clang 的提示:

namespace.cc:8:3: error: no matching constructor for initialization of 'S'
S x = { .member = S::S::E1 };
namespace.cc:3:8: note: candidate constructor (the implicit copy constructor)
      not viable: cannot convert argument of incomplete type 'void' to
      'const S &' for 1st argument
struct S {
       ^
namespace.cc:3:8: note: candidate constructor (the implicit move constructor)
      not viable: cannot convert argument of incomplete type 'void' to 'S &&'
      for 1st argument
struct S {
       ^
namespace.cc:4:3: note: candidate constructor not viable: requires 0 arguments,
      but 1 was provided
  S() { std::cout << "made an S\n"; }
  ^
1 error generated.

和 g++ 的:

namespace.cc:8:28: error: could not convert ‘{E1}’ from ‘<brace-enclosed initializer list>’ to ‘S’
 S x = { .member = S::S::E1 };

这些似乎遵循不同的规则。这是怎么回事?

接下来,让我们再尝试一点虐。这是整个程序:

#include <iostream>

struct S {
  S() { std::cout << "made an S\n"; }
  enum E { E1, E2 } member;
};

int main() {
  std::cout << S::S::S::S::S::E1 << std::endl;
#ifdef DECL
  S::S::S var;
#endif
  return 0;
}

此代码在两个编译器中编译(没有 -DDECL):

$ clang++-3.9 -std=c++11 -Wall -O namespace.cc
$ ./a.out
0
$ g++ -Wall -std=c++11 -O namespace.cc
$ ./a.out
0

(没有 S 在这里构造,尽管 clang 为早期代码中的变量 member 初始值设定项发出了提示。)在 main 中启用变量>,尽管如此,会导致 g++ 失败,但不会导致 clang:

$ clang++-3.9 -std=c++11 -DDECL -Wall -O namespace.cc
$ ./a.out 
0
made an S
$ g++ -std=c++11 -DDECL -Wall -O namespace.cc
namespace.cc: In function ‘int main()’:
namespace.cc:11:3: error: ‘S::S’ names the constructor, not the type
   S::S::S var;
   ^
namespace.cc:11:11: error: expected ‘;’ before ‘var’
   S::S::S var;
           ^
namespace.cc:11:14: error: statement cannot resolve address of overloaded function
   S::S::S var;
              ^

哪个编译器是正确的,为什么?这个“过度限定”名称的规则究竟是什么

最佳答案

Yakk 已经解决了您问题的指定初始化程序部分。我将解决你问题的最后一部分。 S::S::S var(在此上下文中)是否有效?不,根据 class.qual#2 :

In a lookup in which function names are not ignored34 and the nested-name-specifier nominates a class C:

  • if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (Clause [class]), or

  • in a using-declarator of a using-declaration that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id's template-name in the last component of the nested-name-specifier,

the name is instead considered to name the constructor of class C.

为了使其有效,您需要明确地说struct S::S::S var。所以 clang 3.9 是错误的。

此外,Rob 的评论与此处无关。 S::S 只是在类定义中查找时注入(inject)的类名。

关于c++ - 奇怪的 C++ namespace 解析怪癖和 g++ 与 clang++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48123346/

相关文章:

c++ - 为什么 const int& 可以绑定(bind)到 int?

c - freopen() 在缓冲方面的预期行为 (setvbuf())?

c++ - 为什么将括号与默认构造函数一起使用会导致创建变量?

c++ - 与 VS2013 相比,gcc 4.7.2 中的 std::map 实现效率非常低?

c++ - 在 C++ 中更轻松地使用共享指针的方法

c++ - 错误 : cannot convert ‘double*’ to ‘double’ for argument ‘1’ and error: invalid types ‘double[double]’ for array subscript

C# 上下文菜单处理程序

c++ - 我相信这是 clang++ 中与访问类的公共(public)成员函数相关的错误

c++ - 使用分段寻址模式的 16 位系统上 `size_t` 、 `uintptr_t` 、 `intptr_t` 和 `ptrdiff_t` 类型的实际大小是多少?

c++ - 进行测试和变异函数的STL算法