C++等效于对 java 参数/返回类型使用 <T 扩展 Class>
在java中,为了创建一个函数,该函数返回与参数类型相同的对象并扩展某个类,我将键入:
public <T extends MyClass> T foo(T bar) {...}
有没有一个C++等价物?
换句话说,我如何创建一个函数,该函数采用扩展某个类的任何类,并返回相同的类型?(这是为了抽象/纯虚拟类的目的)。
在java中,为了创建一个函数,该函数返回与参数类型相同的对象并扩展某个类,我将键入:
public <T extends MyClass> T foo(T bar) {...}
有没有一个C++等价物?
换句话说,我如何创建一个函数,该函数采用扩展某个类的任何类,并返回相同的类型?(这是为了抽象/纯虚拟类的目的)。
从技术上讲,正如其他答案所示,有一些方法可以在编译时将其限制为特定类型的子类型。但是,大多数时候,您只会这样做
template <typename T> T foo(T bar) {...}
无需指定边界。
在 Java 中,泛型需要边界,因为泛型类或方法的编译与它的任何用法是分开编译的。泛型类或方法被编译为一次,编译成字节码中的单个版本,该版本能够处理调用方向其抛出的任何满足其声明中的边界的参数。
编译器必须在方法主体中对类型的使用(如方法调用、字段访问等)进行类型检查,而不知道是什么,因此您必须提供一个绑定,以便编译器可以满足例如方法调用是有效的,因为它是在满足该边界的所有类型上定义的。例如,如果您在方法的主体中有表达式,则编译器将仅允许您编译,如果类型(以及它的所有子类型)提供该方法;如果未提供任何边界,编译器将抱怨(隐式上限)没有方法。T
T
bar.baz()
MyClass
.baz()
Object
.baz()
C++模板是不同的。模板化的类或函数对于它所用于的每个不同类型的参数进行“实例化”(再次编译)。因此,在为特定函数编译函数体时,编译器知道是什么,并且能够直接对该类型的使用进行类型检查。T
T
因此,如果您在函数体中有表达式,那就没问题了。如果将此函数与扩展的类型一起使用,则它将编译良好,因为此类类型具有 .如果将此函数用于没有 的类型,则它将无法按照该用法进行编译。如果您不小心将函数与不扩展但具有其参数类型和返回类型匹配的类型一起使用,则它也会编译;但这并不一定是一件坏事。C++模板通常不与类型层次结构一起使用,而是与类型需要提供的内容的要求一起使用。因此,例如,排序算法不会要求其容器和/或元素类型扩展某个类型,而是要求容器提供某些功能(例如随机访问下标运算符),而元素类型提供某些功能(例如小于运算符)。bar.baz()
T
MyClass
.baz()
.baz()
MyClass
.baz()
如果您有C++11或更高的价格,我们可以在这里使用enable_if
template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr>
T Foo(T bar)
{
return T();
}
例如:
class MyClass
{
public:
int a = 1;
};
class Derived : public MyClass
{
public:
int b = 2;
};
class NotDerived
{
public:
int b = 3;
};
template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr>
T Foo(T bar)
{
return T();
}
int main()
{
Derived d;
NotDerived nd;
std::cout << Foo(d).b << std::endl;; // works
//std::cout << (Foo(nd)).b << std::endl;; //compiler error
return 0;
}