奇怪的C ++命名空间解析quirk和g ++ vs clang ++

时间:2018-01-06 01:09:14

标签: 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:这个特定虚拟机上的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 ++&#39; s:

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;
              ^

哪种编译器是对的,为什么?究竟 的规则是什么&#34;过度合格&#34;名字?

2 个答案:

答案 0 :(得分:3)

指定的初始值设定项是中C ++的C功能。它们是C ++模式中的常见扩展。显然,此时它们仅限于pod(类C)类型。他们没有惊喜。

通常,不同的编译器会因错误的代码而产生不同的错误,尤其是在使用C ++扩展时。 C ++标准从不强制要求诊断内容(可能有例外,但我不记得是什么)。

gcc有一个扩展名,可以让你命名对象构造函数。这与您对struct命名空间的病态使用相矛盾。

答案 1 :(得分:3)

Yakk已经解决了您问题的指定初始化部分。我将解答你问题的最后部分。 function query($_query, $_params = array()) { global $_DB; # <- Database connection object $query = $_DB->prepare($_query); if (! $query) echo $_DB->errorInfo(); try { $query->execute($_params); } catch (PDOException $e) { die( $e->getMessage() ); } return $query; } (在此上下文中)是否有效?不,按class.qual#2

  

在查找中,函数名称不被忽略 34 和   nested-name-specifier指定一个C类:

     
      
  • 如果在C中查找,在嵌套名称说明符之后指定的名称是C(Clause [class])的注入类名,或

  •   
  • 在using声明的using声明符中,是一个成员声明,​​如果在之后指定的名称   nested-name-specifier与标识符或   simple-template-id在模板的最后一个组件中的模板名称   嵌套名称说明符,

  •   
     

该名称被认为是为C类的构造函数命名。

为了使其有效,您需要明确说出S::S::S var。所以clang 3.9错了。

此外,Rob的评论与此无关。在类定义中查找时,struct S::S::S var只是一个注入的类名。