在成员变量中使用模板类作为模板模板参数时出错

时间:2016-01-17 02:02:25

标签: c++ templates c++11

我在我的C ++项目中使用模板,当使用模板化类型作为模板模板参数时,我遇到了问题。我认为描述它的最好方法是给出一个产生错误的例子:

template <template<class> class P, typename T>
class Foo {
    P<T> baz;
};

template <class T>
class Bar {
    Foo<Bar, T> memberFoo;

    void makeFoo() {
        Foo<Bar, T>* f = new Foo<Bar, T>();
    }
};

Foo<Bar, int> globalFoo;

globalFoo的声明不会导致错误,但memberFoof的声明会导致编译错误:

  

错误:模板模板参数的模板参数必须是类模板   或输入别名模板

只有在Bar类声明中使用Bar作为模板参数时才会出现错误,但同时使用clang和g ++。这看起来似乎会在某处记录,但谷歌搜索不会产生任何问题或其他文档。

这种模板的使用在C ++中是不合法的,还是我误解了如何定义和使用模板?如果C ++ 11标准不允许这种设计架构,我可以使用什么解决方法?

2 个答案:

答案 0 :(得分:2)

问题是在实例化时会产生不完整的类型。 Clang和G ++会产生不同的错误,因为Clang(显然)没有实现C ++ 11规则,当用作模板模板参数时,注入的类名称可以引用类模板本身(Igor的建议没有& #39; t btw。)将Bar更改为::Bar修复了此错误,这使得Clang指出了像G ++那样的不完整错误。将baz更改为P<T>*可以进行编译。

N.B。即使编译,也可能是未定义的行为。我建议重新设计你的课程。

答案 1 :(得分:0)

基于@Igor的评论,我已经找到了解决这个问题的几个方法。基于引用 Bar 在其声明中引用 Bar的特殊化(引用@Igor)这一事实。

请注意,所有这些变通办法都依赖于baz可以声明为指针的事实。 baz不是指针导致@Nir在注释中提到的递归问题并导致错误:

  

字段的类型不完整&f;&lt; Bar&lt; int&gt;,int&gt;&#39;

解决方法1

添加Bar的转发声明并创建模板别名Bar2。为了编译,baz必须是一个指针:

template <template <typename> class P, typename T>
class Foo {
    P<T>* baz;
};

template<typename T> class Bar;
template <typename U> using Bar2 = Bar<U>;

template <class T>
class Bar {
    Foo<Bar2, T> memberFoo;

    void makeFoo() {
        Foo<Bar2, T>* f = new Foo<Bar2, T>();
    }
};

Foo<Bar, int> globalFoo;

模板别名的前向声明和使用强制编译器在定义BarmemberFoo时使用非专业版f,而默认情况下使用非专用版本globalFoo的定义。

解决方法2

此解决方法基于@ user5800314的答案。我觉得没有必要重新陈述他的解决方法,但我觉得值得注意它的工作原因。

我已经阅读了关于注入类名和C ++ 11的similar SO question,但这里的重要区别是我的代码不能在g ++上编译,而他们的代码却是。我不相信这个问题是缺乏注入类名的实现。我相信此解决方法可以修复编译错误,因为使用::Bar而不是Bar会再次强制编译器访问Bar的全局(非专业化)版本,而不是访问本地(专用)版本Bar

解决方法3

Foo指定为具有类(或typename)模板参数而不是模板模板参数,并明确指出在使用Foo模板时正在使用哪种特化。这还要求baz是一个指针,并且它不使用模板类型:

template <class P, typename T>
class Foo {
    P* baz;
};

template <class T>
class Bar {
    Foo<Bar, T> memberFoo;

    void makeFoo() {
        Foo<Bar, T>* f = new Foo<Bar, T>();
    }
};

Foo<Bar<int>, int> globalFoo;

此解决方法通过要求向Foo模板提供特定类来解决模板模板参数的潜在混淆。在某些情况下,此解决方法可能无法使用,但在其他情况下可能是一种优雅的解决方案。例如,在我的情况下,我不需要从Foo之外实例化Bar的实例,所以这是解决编译错误的一种非常好的方法。

PS 我真的很感谢@ user5800314,因为他的解决方法工作,但是我在这里提供了不同的解释,因为我在这里提供的解释是什么我认为是正确的,我不觉得我可以将@ user5800314的答案标记为已接受。