C++ 等效于将 <T extends Class> 用于 java 参数/返回类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30687305/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 17:30:09  来源:igfitidea点击:

C++ equivalent of using <T extends Class> for a java parameter/return type

javac++genericsextends

提问by ricky3350

In java, to make a function that returns an object that is the same type as a parameter and extends a certain class, I would type:

在java中,要创建一个返回与参数类型相同的对象并扩展某个类的函数,我会输入:

public <T extends MyClass> T foo(T bar) {...}

Is there a C++ equivalent of this?

是否有与此等效的 C++?

In other words, how do I make a function that takes any class that extends a certain class, and returns that same type? (This is for the purpose of abstract/pure virtual classes).

换句话说,我如何制作一个函数,它接受任何扩展某个类的类,并返回相同的类型?(这是为了抽象/纯虚拟类的目的)。

采纳答案by AndyG

We can use enable_ifhere if you have C++11 or higher available to you

enable_if如果您有 C++11 或更高版本可用,我们可以在这里使用

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr>
T Foo(T bar)
{
    return T();
}

For example:

例如:

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;
}

Live Demo

现场演示

回答by newacct

Technically, as the other answers show, there are ways to restrict it to subtypes of a certain type at compile time. However, most of the time, you would just do

从技术上讲,正如其他答案所示,有一些方法可以在编译时将其限制为某种类型的子类型。然而,大多数时候,你只会做

template <typename T> T foo(T bar) {...}

without needing to specify a bound.

无需指定界限。

In Java, bounds are needed for generics because the generic class or method is compiled separately from any uses of it. Generic classes or methods are compiled once, into a single version in the bytecode, a single version that is able to handle any arguments that callers throw at it that satisfy the bounds in its declaration.

在 Java 中,泛型需要边界,因为泛型类或方法是与它的任何使用分开编译的。通用类或方法被编译一次,在字节码中成为一个单一版本,这个单一版本能够处理调用者抛出的满足其声明边界的任何参数。

The compiler must type-check uses of the type Tin the body of the method, like method calls, field accesses, etc., without knowing what Tis, so you must provide a bound so the compiler can be satisfied that for example a method call is valid because it is defined on all types that satisfy that bound. For example, if you had the expression bar.baz()in the body of the method, the compiler will only let you compile if the type MyClass(and hence all subtypes of it) provides the method .baz(); if you had provided no bounds, the compiler would complain that Object(the implicit upper bound) has no method .baz().

编译器必须T对方法体中类型的使用进行类型检查,例如方法调用、字段访问等,但不知道是什么T,因此您必须提供一个边界,以便编译器可以满足例如方法调用是有效的,因为它定义在满足该界限的所有类型上。例如,如果您bar.baz()在方法的主体中有表达式,那么编译器只会让您在类型MyClass(以及它的所有子类型)提供方法时进行编译.baz();如果您没有提供任何边界,编译器会抱怨Object(隐式上限)没有 method .baz()

C++ templates are different. The templated class or function is "instantiated" (compiled again) for every different type argument it is used for. So at the time of compiling the body of the function for a particular T, the compiler knows what Tis, and is able to type-check uses of that type directly.

C++ 模板是不同的。对于每个不同类型的参数,模板化的类或函数都被“实例化”(再次编译)。因此,在为特定T的 编译函数体时,编译器知道是什么T,并且能够直接对该类型的使用进行类型检查。

So if you had the expression bar.baz()in the body of the function, that would be fine. If you used this function with Tbeing a type that that extends MyClass, then it will compile fine, because such a type has a .baz(). If you use this function with a type that doesn't have a .baz(), then it will fail to compile at that usage of it. If you accidentally use the function with a type that doesn't extend MyClassbut has a .baz()whose parameter types and return type match the way you are using it, it will compile too; but that's not necessarily a bad thing. C++ templates are not usually used with type hierarchies, but rather with requirements on what the type needs to provide. So for example, a sorting algorithm is not going to require that its container and/or element type extend a certain type, but rather that the container provide certain features (e.g. random access subscript operator), and the element type provide certain features (e.g. a less-than operator).

所以如果你bar.baz()在函数体中有表达式,那就没问题了。如果您将此函数与T扩展类型一起使用MyClass,那么它将很好地编译,因为这样的类型具有.baz(). 如果您将此函数与没有 a 的类型一起使用.baz(),则在使用它时将无法编译。如果您不小心将函数与一个没有扩展MyClass但有扩展的类型一起使用.baz()其参数类型和返回类型与您使用它的方式相匹配,它也会编译;但这不一定是坏事。C++ 模板通常不与类型层次结构一起使用,而是与类型需要提供的内容一起使用。因此,例如,排序算法不会要求其容器和/或元素类型扩展某种类型,而是要求容器提供某些特征(例如随机访问下标运算符),并且元素类型提供某些特征(例如小于运算符)。

回答by Matthew Borger

Since I can't comment on the accepted answer, I'm providing a new answer which builds on it.

由于我无法对已接受的答案发表评论,因此我提供了一个以它为基础的新答案。

The template parameters can be simplified by having the enable_ifcondition become the default type template parameterinstead of nullptr.

可以通过使enable_if条件成为默认类型模板参数而不是来简化模板参数nullptr

template<typename T, typename = std::enable_if<std::is_base_of<MyClass, T>::value>>