简体   繁体   English

gcc和clang上都有奇怪的嵌套类部分特化结果

[英]Weird nested class partial specialization results on both gcc and clang

While writing a small template metaprogramming library for personal use, I came across an interesting problem. 在编写一个供个人使用的小模板元编程库时,我遇到了一个有趣的问题。

Since I was reusing a few partial specializations for some metafunctions, I decided I would put them under a common template class and use tags along with nested partial specialization to provide the differences in behaviour. 由于我正在为一些元函数重用一些部分特化,我决定将它们放在一个公共模板类下,并使用标签和嵌套的部分特化来提供行为上的差异。

The problem is I am getting nonsensical (to me) results. 问题是我得到了荒谬(对我而言)的结果。 Here is a minimal example that showcases what I am trying to do: 这是一个最小的例子,展示了我想要做的事情:

#include <iostream>
#include <cxxabi.h>
#include <typeinfo>

template <typename T>
const char * type_name()
{
    return abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
}

template <typename... Args>
struct vargs {};

namespace details   
{
    template <typename K>
    struct outer
    {
        template <typename Arg>
        struct inner
        {
            using result = Arg;
        };
    };
}

struct tag {};

namespace details
{
    template <>
    template <typename Arg, typename... Args>
    struct outer<tag>::inner<vargs<Arg, Args...>>
    {
        using result = typename outer<tag>::inner<Arg>::result;
    };
}

template <typename T>
using test_t = typename details::outer<tag>::inner<T>::result;

int main()
{
    using t = test_t<vargs<char, int>>;
    std::cout << type_name<t>() << '\n';
    return 0;
}

I am getting vargs<char, int> as output when using the 5.1.0 version of gcc and tag when using the 3.6.0 version of clang. 在使用3.6.0版本的clang时,使用5.1.0版本的gcc和tagvargs<char, int>我得到vargs<char, int>作为输出。 My intention was for the above piece of code to print char so I am pretty baffled by these results. 我的意图是上面的代码打印char所以我对这些结果感到非常困惑。

Is the above piece of code legal or does it exhibit undefined behavior? 上面的代码是合法的还是表现出未定义的行为? If it's legal what is the expected behavior according to the standard? 如果合法,根据标准的预期行为是什么?

Your code is correct; 你的代码是正确的; out-of-class implicitly instantiated class template member class template partial specializations are intended to be allowed by the Standard, as long as they are defined early enough. 类外隐式实例化的类模板成员类模板只要它们足够早地定义,标准就允许部分特化。

First, let's try for a minimal example - noting by the way that there's nothing here that requires C++11: 首先,让我们尝试一个最小的例子 - 注意这里没有什么需要C ++ 11的方式:

template<class T> struct A {
  template<class T2> struct B { };
};
// implicitly instantiated class template member class template partial specialization
template<> template<class T2>
  struct A<short>::B<T2*> { };
A<short>::B<int*> absip;    // uses partial specialization?

As noted elsewhere MSVC and ICC use the partial specialization as expected; 如其他地方所述,MSVC和ICC按预期使用部分专业化; clang selects the partial specialization but messes up its type parameters, aliasing T2 to short instead of int ; clang选择部分特化但是弄乱了它的类型参数,将T2混叠为short而不是int ; and gcc ignores the partial specialization entirely. 并且gcc完全忽略了部分特化。

Why out-of-class implicitly instantiated class template member class template partial specialization is allowed 为什么允许类外隐式实例化的类模板成员类模板部分特化

Put simply, none of the language that permits other forms of class template member class template definitions excludes out-of-class implicitly instantiated class template member class template partial specialization. 简而言之,允许其他形式的类模板成员类模板定义的语言都不会排除类外隐式实例化的类模板成员类模板部分特化。 In [temp.mem] , we have: [temp.mem]中 ,我们有:

1 - A template can be declared within a class or class template; 1 - 模板可以在类或类模板中声明; such a template is called a member template. 这样的模板称为成员模板。 A member template can be defined within or outside its class definition or class template definition. 可以在类定义或类模板定义之内或之外定义成员模板。 [...] [...]

A class template partial specialization is a template declaration ( [temp.class.spec] /1). 类模板部分特化是模板声明( [temp.class.spec] / 1)。 In the same paragraph, there is an example of out-of-class nonspecialized class template member class template partial specialization ( [temp.class.spec] /5): 在同一段中,有一个类外非专用类模板成员类模板部分特化( [temp.class.spec] / 5)的例子

template<class T> struct A {
  struct C {
    template<class T2> struct B { };
  };
};
// partial specialization of A<T>::C::B<T2>
template<class T> template<class T2>
  struct A<T>::C::B<T2*> { };
A<short>::C::B<int*> absip; // uses partial specialization

There is nothing here to indicate that the enclosing scope cannot be an implicit specialization of the enclosing class template. 这里没有任何内容表明封闭范围不能是封闭类模板的隐式特化。

Similarly, there are examples of in-class class template member class template partial specialization and out-of-class implicitly instantiated class template member class template full specialization ( [temp.class.spec.mfunc] /2): 类似地,有类内模板成员类模板部分特化和类外隐式实例化类模板成员类模板完全特化( [temp.class.spec.mfunc] / 2)的示例

template<class T> struct A {
  template<class T2> struct B {}; // #1
  template<class T2> struct B<T2*> {}; // #2
};
template<> template<class T2> struct A<short>::B {}; // #3
A<char>::B<int*> abcip; // uses #2
A<short>::B<int*> absip; // uses #3
A<char>::B<int> abci; // uses #1

(clang (as of 3.7.0-svn235195) gets the second example wrong; it selects #2 instead of #3 for absip .) (clang(从3.7.0-svn235195开始)得到第二个例子错误;它选择#2而不是#3作为absip 。)

While this does not explicitly mention out-of-class implicitly instantiated class template member class template partial specialization, it does not exclude it either; 虽然这没有明确提到类外隐式实例化的类模板成员类模板部分特化,但它也不排除它; the reason it isn't here is that it's irrelevant for the particular point being made, which is about which primary template or partial template specializations are considered for a particular specialization. 它不在这里的原因是它与特定点无关,这与特定专业化考虑哪些主要模板或部分模板特化。

Per [temp.class.spec] : Per [temp.class.spec]

6 - [...] when the primary template name is used, any previously-declared partial specializations of the primary template are also considered. 6 - [...]当使用主模板名称时,还会考虑主模板的任何先前声明的部分特化。

In the above minimal example, A<short>::B<T2*> is a partial specialization of the primary template A<short>::B and so should be considered. 在上面的最小例子中, A<short>::B<T2*>是主模板A<short>::B ,因此应该考虑。

Why it might not be allowed 为什么不允许这样做

In other discussion we've seen mention that implicit instantiation (of the enclosing class template) could result in implicit instantiation of the definition of the primary template specialization to take place, resulting in an ill-formed program NDR ie UB; 在其他讨论中,我们已经看到提到隐式实例化(封闭类模板)可能导致主模板特化的定义的隐式实例化,从而导致格式错误的程序NDR,即UB; [templ.expl.spec] : [templ.expl.spec]

6 - If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; 6 - 如果模板,成员模板或类模板的成员是明确专用的,则应在第一次使用该特化之前声明该特化,这将导致发生隐式实例化,在每个翻译单元中使用发生; no diagnostic is required. 无需诊断。 [...] [...]

However, here the class template member class template is not used before it is instantiated. 但是,这里的类模板成员类模板在实例化之前不会使用。

What other people think 其他人的想法

In DR1755 (active) , the example given is: DR1755(活动)中 ,给出的示例是:

template<typename A> struct X { template<typename B> struct Y; };
template struct X<int>;
template<typename A> template<typename B> struct X<A>::Y<B*> { int n; };
int k = X<int>::Y<int*>().n;

This is considered problematic only from the point of view of the existence of the second line instantiating the enclosing class. 仅从存在实例化封闭类的第二行的观点来看,这被认为是有问题的。 There was no suggestion from the submitter (Richard Smith) or from CWG that this might be invalid even in the absence of the second line. 提交者(理查德史密斯)或CWG没有提出即使没有第二行也可能无效的建议。

In n4090 , the example given is: n4090中 ,给出的例子是:

template<class T> struct A {
  template<class U> struct B {int i; }; // #0
  template<> struct B<float**> {int i2; }; // #1
  // ...
};
// ...
template<> template<class U> // #6
struct A<char>::B<U*>{ int m; };
// ...
int a2 = A<char>::B<float**>{}.m; // Use #6 Not #1

Here the question raised is of precedence between an in-class class template member class template full specialization and an out-of-class class template instantiation member class template partial specialization; 这里提出的问题是在类内模板成员类模板完全特化和类外模板实例化成员类模板部分特化之间的优先级; there is no suggestion that #6 would not be considered at all. 没有人建议根本不考虑#6

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

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