简体   繁体   English

CRTP与模板模板参数

[英]CRTP with Template Template Arguments

The following code doesn't compile... 以下代码无法编译......

namespace {
    template<typename T, template<typename> class D>
    struct Base {
        Base(const T& _t) : t(_t) { }
        T t;
    };

    template<typename T>
    struct Derived : Base<T, Derived> {
        Derived(const T& _t) : Base<T, Derived>(_t) { }
    };
}

int main(int argc, char* argv[]) {
    Derived<int> d(1);
    return 0;
}

There is a compilation error on the line - Derived(const T& _t) : Base<T, Derived>(_t) { } 该行有一个编译错误 - Derived(const T& _t) : Base<T, Derived>(_t) { }

Error C3200 '`anonymous-namespace'::Derived': invalid template argument for template parameter 'D', expected a class template 错误C3200''anonymous-namespace':: Derived':模板参数'D'的模板参数无效,需要一个类模板

This works if I provide any other class that has template argument instead of Derived itself 如果我提供任何其他具有模板参数而不是Derived本身的类,则此方法有效

template<typename T>
struct Other {

};
template<typename T>
struct Derived : Base<T, Other> {
    Derived(const T& _t) : Base<T, Other>(_t) { }
};

Tl;dr: the most portable and least extensive way to get around that problem seems to be using the qualified name ::Derived in your example: Tl; dr:解决该问题的最便携和最不广泛的方法似乎是在您的示例中使用限定名称::Derived

template<typename T>
struct Derived : Base<T, Derived>
{
  Derived(const T& _t) : Base<T, ::Derived>(_t) { }
};

Why? 为什么?

The Problem is compiler non-conformance to C++11. 问题是编译器不符合C ++ 11。

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). 与普通(非模板)类一样,类模板具有注入类名(第9节)。 The injected class-name can be used as a template-name or a type-name. 注入的类名可以用作模板名或类型名。 When it is used with a template-argument-list, as a template-argument for a template template-parameter , or as the final identifier in the elaborated-typespecifier of a friend class template declaration, it refers to the class template itself. 当它与template-argument-list一起使用时, 作为模板模板参数的模板参数 ,或作为友元类模板声明的详细说明类型说明符中的最终标识符, 它引用类模板本身。

Thus your code should compile but unfortunately, all compilers I tested (clang 3.7, Visual Studio 2015 and g++5.3) refuse to do it. 因此,您的代码应该编译,但遗憾的是,我测试的所有编译器(clang 3.7,Visual Studio 2015和g ++ 5.3)都拒绝这样做。

Afaik, you should be able to denote the template in various ways, inside the Derived definition: Afaik,你应该能够在Derived定义中以各种方式表示模板:

  • Using the injected name directly Derived 直接使用注入的名称Derived
  • Qualified name of the class template ::Derived 类模板的合格名称::Derived
  • Using the injected class name, designating it as a template name Derived<T>::template Derived 使用注入的类名称,将其指定为模板名称Derived<T>::template Derived
  • Using a qualified name, again designating it as a template name ::template Derived 使用限定名称,再次将其指定为模板名称::template Derived

The compilation status of those compilers regarding those four options is as follows (using the anonymus namespace; where + = successful compilation): 这些编译器关于这四个选项的编译状态如下(使用anonymus命名空间;其中+ =成功编译):

+------------------------------+----------+---------+-----------+
|           Method             | MSVS2015 | g++ 5.3 | clang 3.7 |
+------------------------------+----------+---------+-----------+
| Derived                      |    -     |    -    |     -     |
| ::Derived                    |    +     |    +    |     +     |
| Derived<T>::template Derived |    -     |    -    |     +     |
| ::template Derived           |    +     |    -    |     +     |
+------------------------------+----------+---------+-----------+

When giving the namespace the name X , the picture changes a little (namely g++ now accepts X::template Derived whereas it rejected ::template Derived ): 当给名称空间命名为X ,图片会稍微改变(即g++现在接受X::template Derived而它拒绝::template Derived ):

+---------------------------------+----------+---------+-----------+
|            Method               | MSVS2015 | g++ 5.3 | clang 3.7 |
+---------------------------------+----------+---------+-----------+
| Derived                         |    -     |    -    |     -     |
| X::Derived                      |    +     |    +    |     +     |
| X::Derived<T>::template Derived |    -     |    -    |     +     |
| X::template Derived             |    +     |    +    |     +     |
+---------------------------------+----------+---------+-----------+

In a class template, the injected-class-name ( Derived in your example) can be both a type name and a template name. 在类模板中,inject-class-name(在您的示例中为Derived )可以是类型名称和模板名称。 The standard specifies that it should be considered to name the template when used as an argument to a template template parameter (so your code should work), but unfortunately some compilers haven't yet implemented this. 该标准指定在用作模板模板参数的参数时应该考虑将模板命名(因此您的代码应该起作用),但遗憾的是一些编译器尚未实现此功能。

One workaround is to use a qualified name, so that you don't use the injected-class-name but directly name the template: 一种解决方法是使用限定名称,这样您就不会使用inject-class-name,而是直接命名模板:

template<typename T>
struct Derived : Base<T, Derived> {
    Derived(const T& _t) : Base<T, ::Derived>(_t) { }
};

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

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