简体   繁体   English

成员在不同名称空间中的别名声明专门化

[英]Member specialization of alias declaration in different namespaces

I just encountered a strange difference in behavior between clang and gcc where I wanted to compile code which looks similar to the following: 我刚刚在clang和gcc之间遇到了一个奇怪的区别,我想编译看起来类似于以下内容的代码:

namespace n1 {
  template <class T1, class T2>
  struct MyTemplate {
    struct Inner {};
  };
}

using namespace n1;
namespace n2 {
  using MyClass = MyTemplate<int, int>;
}

namespace n1 {
  using n2::MyClass;
  template<> struct MyClass::Inner {
    int member;
  };

  MyClass::Inner inner{0};
}

Clang happily compiles this: Clang愉快地编写了这个:

$ clang++ -std=c++11 -c -o alias_specialization.o alias_specialization.cc

but gcc throws the following error: 但是gcc会抛出以下错误:

$ g++ -std=c++11 -c -o alias_specialization.o alias_specialization.cc

alias_specialization:15:30: error: declaration of ‘struct n1::MyTemplate<int, int>::Inner’ in namespace ‘n1’ which does not enclose ‘using MyClass = struct n1::MyTemplate<int, int>’
   template<> struct MyClass::Inner {

I know I can just write the full name of the original type ( MyTemplate<int, int> ) instead of MyClass in line 15. But I'm simply wondering which of the two compilers is "right". 我知道我可以在第15行写出原始类型的全名( MyTemplate<int, int> )而不是MyClass 。但我只是想知道两个编译器中的哪一个是“正确的”。 The exact compilers in use are: 使用的确切编译器是:

$ clang++ --version
clang version 4.0.0

$ g++ --version
g++ (GCC) 6.3.1 20170306

Disclaimer : 免责声明 GCC GCC Clang is right (as @suluke pointed out, using the findings below). Clang是对的(正如@suluke指出的那样,使用下面的发现)。

I found the following passages in the C++14 standard, but I am sure the same applies to C++11: 我在C ++ 14标准中找到了以下段落,但我确信这同样适用于C ++ 11:

Part I: Specialization & Instantiation of Templates 第一部分:模板的专业化和实例化

inline namespace (§7.3.1 cl. 8): inline namespace (第7.3.1节,第8节):

Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace. 内联命名空间的成员可以在大多数方面使用,就像它们是封闭命名空间的成员一样。 Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lookup (3.4.2) whenever one of them is, and a using- directive (7.3.4) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (7.3.1.1). 具体来说,只要其中一个命名空间及其封闭命隐式插入到封闭的命名空间中,就像未命名的命名空间一样(7.3.1.1)。 Furthermore, each member of the inline namespace can subsequently be partially specialized (14.5.5), explicitly instantiated (14.7.2), or explicitly specialized (14.7.3) as though it were a member of the enclosing namespace. 此外,内联命名空间的每个成员随后可以部分专用(14.5.5),显式实例化(14.7.2)或显式专用(14.7.3),就像它是封闭命名空间的成员一样。 Finally, looking up a name in the enclosing namespace via explicit qualification (3.4.3.2) will include members of the inline namespace brought in by the using-directive even if there are declarations of that name in the enclosing namespace. 最后,通过显式限定(3.4.3.2)查找封闭命名空间中的名称将包括using-directive引入的内联命名空间的成员,即使在封闭命名空间中存在该名称的声明也是如此。

Explicit instantiation (§ 14.7.2 cl. 3) 显式实例化(§14.7.2cl.3)

If the explicit instantiation is for a class or member class, the elaborated-type-specifier in the declaration shall include a simple-template-id. 如果显式实例化是针对类或成员类的,则声明中的elaborated-type-specifier应包含simple-template-id。 If the explicit instantiation is for a function or member function, the unqualified- id in the declaration shall be either a template-id or, where all template arguments can be deduced, a template-name or operator-function-id. 如果显式实例化是针对函数或成员函数的,则声明中的unqualified-id应为template-id,或者可以推导出所有模板参数的模板名称或operator-function-id。 [Note: The declaration may declare a qualified-id, in which case the unqualified-id of the qualified-id must be a template-id. [注意:声明可以声明qualified-id,在这种情况下,qualified-id的unqualified-id必须是template-id。 —end note] If the explicit instantiation is for a member function, a member class or a static data member of a class template specialization, the name of the class template specialization in the qualified-id for the member name shall be a simple-template-id. -end note]如果显式实例化是用于成员函数,成员类或类模板特化的静态数据成员,则成员名称的qualified-id中的类模板特化的名称应为简单模板-ID。 If the explicit instantiation is for a variable, the unqualified-id in the declaration shall be a template-id. 如果显式实例化是针对变量的,则声明中的unqualified-id应为template-id。 An explicit instantiation shall appear in an enclosing namespace of its template. 显式实例化应出现在其模板的封闭命名空间中。 If the name declared in the explicit instantiation is an unqualified name, the explicit instantiation shall appear in the namespace where its template is declared or, if that namespace is inline (7.3.1), any namespace from its enclosing namespace set. 如果在显式实例化中声明的名称是非限定名称,则显式实例化应出现在声明其模板的命名空间中,或者如果该命名空间是内联(7.3.1),则来自其封闭命名空间集合中的任何命名空间。

Explicit instantiation (§ 14.7.2 cl. 6) 显式实例化(§14.7.2cl.6)

An explicit instantiation of a class, function template, or variable template specialization is placed in the namespace in which the template is defined. 类,函数模板或变量模板特化的显式实例化放置在定义模板的命名空间中。 An explicit instantiation for a member of a class template is placed in the namespace where the enclosing class template is defined. 类模板成员的显式实例化放置在定义封闭类模板的命名空间中。 An explicit instantiation for a member template is placed in the namespace where the enclosing class or class template is defined. 成员模板的显式实例化放置在定义封闭类或类模板的命名空间中。 [ Example: [例如:

namespace N {
    template<class T> class Y { void mf() { } };
  }
  template class Y<int>;  // error: class template Y not visible 
                          // in the global namespace

  using N::Y;
  template class Y<int>; // error: explicit instantiation outside of the 
                         // namespace of the template


  template class N::Y<char*>;  // OK: explicit instantiation in namespace N
  template void N::Y<double>::mf();  // OK: explicit instantiation
                                     // in namespace N

— end example ] - 结束例子]

Part II: Typedef, Aliasing 第二部分:Typedef,Aliasing

What is typedef or an alias? 什么是typedef或别名?

§7.1.3 cl. §7.1.3cl。 1 states: 1州:

[...] A name declared with the typedef specifier becomes a typedef-name. [...]使用typedef说明符声明的名称将成为typedef-name。 Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in the way described in Clause 8. A typedef-name is thus a synonym for another type. 在其声明的范围内,typedef-name在语法上等同于关键字,并按照第8章中描述的方式命名与标识符关联的类型。因此,typedef-name是另一种类型的同义词。 A typedef-name does not introduce a new type the way a class declaration (9.1) or enum declaration does. typedef-name不会像类声明(9.1)或枚举声明那样引入新类型。

§7.1.3 cl. §7.1.3cl。 2 states: 2州:

A typedef-name can also be introduced by an alias-declaration . 也可以通过别名声明引入typedef-name。 The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. using关键字后面的标识符变为typedef-name,并且该标识符后面的可选attribute-specifier-seq属于该typedef-name。 It has the same semantics as if it were introduced by the typedef specifier. 它具有与typedef说明符引入的语义相同的语义。 In particular, it does not define a new type and it shall not appear in the type-id. 特别是,它没有定义新类型 ,它不应出现在type-id中。

Part III: Testing Your Case 第三部分:测试你的案例

Following your logic the following code should compile and produce 2 different types. 遵循您的逻辑,以下代码应编译并生成2种不同的类型。 I tested it with Clang, which is the compiler in question. 我用Clang测试了它,这是有问题的编译器。 Clang produces the same type as standard it requires. Clang生产的标准与其要求的标准相同。

#include <iostream>

namespace n1 {
  template <class T1, class T2>
  struct MyTemplate {
    struct Inner {};
  };
}

using namespace n1;
namespace n2 {
  using MyClass = MyTemplate<int, int>;
}

namespace n3 {
  using MyClass = MyTemplate<int, int>;
}

namespace n1 {
  using n2::MyClass;
  template<> struct MyClass::Inner {
    int member;
  };

  MyClass::Inner inner{0};
}

namespace n4{
  using n3::MyClass;

  MyClass::Inner inner{0};
}

int main()
{
  using namespace std;

  cout << typeid(n1::inner).name() << endl;
  cout << typeid(n4::inner).name() << endl;

  return 0;
}

Compilation 汇编

c++ -std=c++14 typedef-typeid.cpp -O3 -o compiled.bin

OR 要么

c++ -std=c++11 typedef-typeid.cpp -O3 -o compiled.bin

Output in both cases 两种情况下的输出

N2n110MyTemplateIiiE5InnerE
N2n110MyTemplateIiiE5InnerE

As it turns out that in this context typedef and using are equivalent, we can use typedef instead of using . 事实证明,在这种情况下, typedefusing是等价的,我们可以使用typedef而不是using

§7.3.3 cl. §7.3.3cl。 1 points this out: 1分指出:

[...] If a using-declaration names a constructor (3.4.3.1), it implicitly declares a set of constructors in the class in which the using-declaration appears (12.9); [...]如果using声明命名一个构造函数(3.4.3.1),它会在类中隐式声明一组使用声明出现的构造函数(12.9); otherwise the name specified in a using-declaration is a synonym for a set of declarations in another namespace or class . 否则,using-declaration中指定的名称是另一个名称空间或类中的一组声明的同义词

#include <iostream>
#include <typeinfo>

namespace n1 {
  template <class T1, class T2>
  struct MyTemplate {
    struct Inner {};
  };
}

using namespace n1;
namespace n2 {
  typedef MyTemplate<int, int> MyClass;
}


namespace n1 {
  typedef n2::MyClass MyClass;

  template<> struct MyClass::Inner {
    int member;
  };

  MyClass::Inner inner{0};
}

int main()
{
  using namespace std;

  cout << typeid(n1::inner).name() << endl;

  return 0;
}

And GCC perfectly compiles the example, without blaming anything. GCC完美地编写了这个例子,没有任何指责。

In addition it'd be interesting to find out how the transitiveness with using declaration works with GCC. 另外,了解using声明的传递性如何与GCC一起using会很有趣。

Here is the modified example: 这是修改后的例子:

#include <iostream>
#include <typeinfo>

namespace n1 {
  template <class T1, class T2>
  struct MyTemplate {
    struct Inner {};
  };
}


using namespace n1;
namespace n2 {
  using MyClass = MyTemplate<int, int>;

}

namespace n3
{
  using n2::MyClass;
}


namespace n1 {
  typedef n3::MyClass MyClass;

  template<> struct MyClass::Inner {
    int member;
  };

  MyClass::Inner inner{0};
}

int main()
{
  using namespace std;

  cout << typeid(n1::inner).name() << endl;

  return 0;
}

And again both Clang and GCC happily compile it. Clang和GCC再次愉快地编译它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM