在java中,要创建一个返回与参数类型相同的对象并扩展某个类的函数,我会输入:
public <T extends MyClass> T foo(T bar) {...}
是否有与此类似的C ++?
换句话说,我如何创建一个函数,它接受任何扩展某个类的类,并返回相同的类型? (这是为了抽象/纯虚拟类的目的)。
答案 0 :(得分:13)
如果你有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;
}
答案 1 :(得分:12)
从技术上讲,正如其他答案所示,有一些方法可以在编译时将其限制为某种类型的子类型。但是,大多数情况下,你只会这样做
template <typename T> T foo(T bar) {...}
无需指定绑定。
在Java中,泛型需要边界,因为泛型类或方法是与它的任何使用分开编译的。通用类或方法被编译一次,成为字节码中的单个版本,单个版本能够处理调用者在其中抛出的满足其声明中的边界的任何参数。
编译器必须在方法体中键入检查类型T
的使用,如方法调用,字段访问等,而不知道T
是什么,所以你必须提供一个绑定所以编译器可以满足,例如方法调用是有效的,因为它是在满足该边界的所有类型上定义的。例如,如果在方法体中有表达式bar.baz()
,那么只有类型MyClass
(及其所有子类型)提供方法{{1}时,编译器才会编译。 };如果你没有提供边界,编译器会抱怨.baz()
(隐式上限)没有方法Object
。
C ++模板不同。模板化的类或函数是&#34;实例化的&#34; (再次编译)用于它所使用的每个不同类型的参数。因此,在为特定.baz()
编译函数体时,编译器知道T
是什么,并且能够直接对该类型的使用进行类型检查。
因此,如果你在函数体中有表达式T
,那就没问题了。如果您使用此函数且bar.baz()
是扩展T
的类型,那么它将编译正常,因为此类型具有MyClass
。如果您将此函数用于没有.baz()
的类型,那么它将无法在使用它时进行编译。如果您不小心使用的函数类型不会扩展.baz()
但其MyClass
的参数类型和返回类型与您使用它的方式相匹配,那么它也会编译;但这并不一定是坏事。 C ++模板通常不与类型层次结构一起使用,而是与需要提供的类型有关。因此,例如,排序算法不要求其容器和/或元素类型扩展某种类型,而是容器提供某些特征(例如随机访问下标操作符),并且元素类型提供某些特征(例如,一个小于运营商。)
答案 2 :(得分:4)
由于我不能对接受的答案发表评论,我提供了一个基于它的新答案。
enable_if
条件变为default type template parameter而不是nullptr
,可以简化模板参数。
template<typename T, typename = std::enable_if<std::is_base_of<MyClass, T>::value>>
答案 3 :(得分:3)
也想知道同样的问题,那就是通过概念的C ++ 20方法。
#include <iostream>
#include <utility>
#include <concepts>
using namespace std;
class MyClass
{
public:
inline virtual void Print() const noexcept { cout << "Myclass" << endl; }
};
class Derived : public MyClass
{
public:
inline virtual void Print() const noexcept override { cout << "Derived" << endl; }
};
class NotDerived
{};
创建一个名称为CheckType的概念 当概念的参数Type为BaseClass或DerivedClass时:BaseClass。
template <class Type, class BaseClass>
concept CheckType = std::is_base_of<BaseClass, Type>::value;
创建一个通用类,其中的T类型需要为MyClass或Derived:MyClass类型。
template <class T>
requires CheckType<T, MyClass>
class OnlyMyClass
{
private:
T Instance;
public:
inline void Print() const noexcept { Instance.Print(); } // Print
};
int main()
{
//OnlyMyClass<NotDerived> O1; // error, the associated constraints are not satisfied.
OnlyMyClass<MyClass> O2; // nice!
OnlyMyClass<Derived> O3; // nice!
O2.Print();
O3.Print();
return 0;
} // main