C++模板多态性

Rus*_*rse 29 c++ polymorphism templates

我有这种类的结构.

class Interface{
...
}

class Foo : public Interface{
...
}

template <class T>
class Container{
...
}
Run Code Online (Sandbox Code Playgroud)

我有一些其他类Bar的构造函数.

Bar(const Container<Interface> & bar){
...
}
Run Code Online (Sandbox Code Playgroud)

当我以这种方式调用构造函数时,我得到"无匹配函数"错误.

Container<Foo> container ();

Bar * temp = new Bar(container);
Run Code Online (Sandbox Code Playgroud)

怎么了?模板不是多态的吗?

Luc*_*lle 39

我认为你需要的确切术语是"模板协方差",这意味着如果B继承自A,那么某种方式T<B>继承自T<A>.这不是C++中的情况,也不是Java和C#泛型*.

避免模​​板协方差是有充分理由的:这将简单地删除模板类中的所有类型安全性.让我用以下示例解释一下:

//Assume the following class hierarchy
class Fruit {...};

class Apple : public Fruit {...};

class Orange : public Fruit {...};

//Now I will use these types to instantiate a class template, namely std::vector
int main()
{
    std::vector<Apple> apple_vec;
    apple_vec.push_back(Apple()); //no problem here

    //If templates were covariant, the following would be legal
    std::vector<Fruit> & fruit_vec = apple_vec;

    //push_back would expect a Fruit, so I could pass it an Orange
    fruit_vec.push_back(Orange()); 

    //Oh no! I just added an orange in my apple basket!
}
Run Code Online (Sandbox Code Playgroud)

因此,无论A和B之间的关系如何,您都应该考虑T<A>并将其T<B>视为完全不相关的类型.

那你怎么能解决你所面临的问题呢?在Java和C#中,您可以分别使用有界通配符约束:

//Java code
Bar(Container<? extends Interface) {...}

//C# code
Bar<T>(Container<T> container) where T : Interface {...}
Run Code Online (Sandbox Code Playgroud)

下一个C++标准(称为C++ 1x(以前称为C++ 0x))最初包含一个名为Concepts的更强大的机制,它可以让开发人员对模板参数强制执行语法和/或语义要求,但不幸的是推迟到以后的日子.但是,Boost有一个您可能感兴趣的概念检查库.

然而,对于遇到的问题,概念可能有点过分,使用@gf提出的简单静态断言可能是最好的解决方案.

*更新:自.Net Framework 4以来,可以将通用参数标记为协变或逆变.


Geo*_*che 11

这里有两个问题:默认结构有形式MyClass c;; 使用括号,它看起来像编译器的函数声明.

另一个问题是它Container<Interface>只是一个不同的类型Container<Foo>- 你可以做以下事实而不是实际获得多态性:

Bar::Bar(const Container<Interface*>&) {}

Container<Interface*> container;
container.push_back(new Foo);
Bar* temp = new Bar(container);
Run Code Online (Sandbox Code Playgroud)

或者当然,你可以Bar像Kornel所展示的那样使它或它的构造函数成为模板.

如果你真的想要一些类型安全的编译时多态,你可以使用Boost.TypeTraits is_base_of或一些等价物:

template<class T>
Bar::Bar(const Container<T>& c) {
    BOOST_STATIC_ASSERT((boost::is_base_of<Interface, T>::value));
    // ... will give a compile time error if T doesn't 
    // inherit from Interface
}
Run Code Online (Sandbox Code Playgroud)

  • *错误 - 第1行:占有后跟名词后面是占有欲 - 预期的解释性陈述.* (2认同)

Kor*_*icz 5

不.想象一下,容器参数被"硬编码"到它定义的类中(实际上它是如何工作的).因此容器类型Container_Foo是不兼容的Container_Interface.

你会尝试的是这个:

template<class T>
Bar(const Container<T> & bar){
...
}
Run Code Online (Sandbox Code Playgroud)

然而,你放松了直接类型检查.

实际上STL方式(可能更有效和通用)是可行的

template<class InputIterator>
Bar(InputIterator begin, InputIterator end){
...
}
Run Code Online (Sandbox Code Playgroud)

...但我假设您没有在容器中实现迭代器.