即使模板 class 实现了抽象方法,C++ 中的 Class 仍然是抽象的
Class in c++ is still abstract even if the template class implements the abstract methods
我想知道如果编译器认为模板 class 的实例仍然是抽象的,即使派生自的 class 实现了这个抽象方法,这是否符合标准。
我的编译器是 Microsoft C/C++ Compiler 18.00.40629 (Visual Studio 2013)
关注 class 的客人:
class MyClass {
virtual void Method() = 0;
};
template <typename T>
class MyClassT : public MyClass, public T {
};
class MyTemplateData {
void Method() {
}
};
MyClassT<MyTemplateData> MyInstance;
根据我的理解,class MyClassT<MyTemplateData>
是实现所有抽象方法的有效实例,因为 MyClassT 派生自 MyClass
和 MyTemplateData
。为了能够通过保持所需的行为来编译此 class,我应该更改什么?
有一个解决方案,但让我解释一下为什么我不想跟随:
template <typename T>
class MyClassT : public MyClass, public T {
void Method() {
T::Execute();
}
};
在上面的例子中,它确实可以编译,但是这假设我的 T
class 总是实现 Method
。然而,我想要的是在 MyClassT
中有一个空的 Method
而不是如果 T
class 实现它应该被 T::Method
覆盖。
所以下面也会编译,但是 T::Method
永远不会被调用。
template <typename T>
class MyClassT : public MyClass, public T {
void Method() {
}
};
MyClass* Interface = new MyClassT<MyTemplateData>();
Interface->Method(); // <- here, MyTemplateData::Method() gets never called
是否有解决方案,或者这是编译器特定的错误?
编辑:再举一个例子,这次编译器应该确切地知道派生的class实现了所需的抽象方法。
struct InterfaceA {
virtual void MethodA() = 0;
};
struct InterfaceAB : InterfaceA {
virtual void MethodB() = 0;
};
class ImplA : public virtual InterfaceA {
public:
void MethodA() {
}
};
class ImplAB : public virtual InterfaceAB, public ImplA {
void MethodB() {
}
};
编译器希望 class ImplAB
实现 MethodA
,因为 InterfaceAB
需要同时实现 MethodA
和 MethodB
。但是,由于 ImplAB
也派生自 ImplA
,并且编译器确切地知道 ImplA
实现了该接口,所以根本没有问题。
至少应该有一个关键字来提示编译器在哪里实现了抽象方法,如下例所示。我看不出有任何理由至少在以任何方式使用 VTABLE 时应该避免这种情况。
class ImplABCD : public implements InterfaceABCD, public ImplA, public ImplB, public ImplC, public ImplC {
};
此 implements 关键字表示 InterfaceABCD
中的所有抽象方法将在 ImplABCD
中实现,或者已经在一个中实现继承的 classes。如果存在签名歧义,那么当从具有相同基础 classes.
的多个 classes 继承时,c++ 规范已经处理了这种情况。
如果您不想使用我们的@ForEveR 解决方案,那么您可以执行以下操作。
使用 SFINAE std::enable_if。
内容如下:
template <typename T>
struct MyClassT : public MyClass, public T
{
typename std::enable_if<has_func_method<T>::value, void>::type Method()
{
T::Method();
}
typename std::enable_if<!has_func_method<T>::value, void>::type Method()
{
//do nothing
}
};
编辑:
您可以在 class 本身而不是内部使用 SFINAE。
那样可能会更好。
这是一个工作示例:
// Example program
#include <iostream>
#include <string>
struct MyClass
{
virtual void Method() = 0;
};
template< typename C, typename = void >
struct has_func_method
: std::false_type
{};
template< typename C>
struct has_func_method< C, typename std::enable_if<
std::is_same<
decltype( std::declval<C>().Method()),
void
>::value
>::type >
: std::true_type
{};
template <typename T, bool has_method = has_func_method<T>::value>
struct MyClassT : public MyClass, public T
{
};
template <typename T>
struct MyClassT<T, false> : public MyClass, public T
{
void Method()
{
//do nothing
std::cout << "doing nothing" << std::endl;
}
};
template <typename T>
struct MyClassT<T, true> : public MyClass, public T
{
void Method()
{
std::cout << "calling T::Method" << std::endl;
T::Method();
}
};
struct DataWithoutMethod
{
};
struct DataWithMethod
{
void Method()
{
std::cout << "method called" << std::endl;
}
};
int main()
{
MyClassT<DataWithoutMethod> data;
data.Method();
MyClassT<DataWithMethod> data2;
data2.Method();
}
I want to know if it is standard compliant behaviour
是的。
if the compiler thinks that the instance of the template class is still abstract
是。
even if the class to derive from implements this abstract methods.
没有。
你没有覆盖MyClass::Method
; T::Method
无关。
让我们暂时摆脱模板,因为它们无关紧要并且会分散我们对问题的注意力:
class MyClass {
virtual void Method() = 0;
};
class MyTemplateData {
void Method() {
}
};
class MyClassT : public MyClass, public MyTemplateData {
};
这是继承树:
MyClass MyTemplateData
\ /
\ /
MyClassT
Method
必须在左侧实现;也就是说,在 MyClass
、MyClassT
、 或介于两者之间的 。右边是一个不相关的继承分支:当你创建MyTemplateData::Method
时,这并没有覆盖MyClass::Method
,因为MyTemplateData
和MyClass
之间没有继承关系。
覆盖仅适用于直线。
我想知道如果编译器认为模板 class 的实例仍然是抽象的,即使派生自的 class 实现了这个抽象方法,这是否符合标准。
我的编译器是 Microsoft C/C++ Compiler 18.00.40629 (Visual Studio 2013)
关注 class 的客人:
class MyClass {
virtual void Method() = 0;
};
template <typename T>
class MyClassT : public MyClass, public T {
};
class MyTemplateData {
void Method() {
}
};
MyClassT<MyTemplateData> MyInstance;
根据我的理解,class MyClassT<MyTemplateData>
是实现所有抽象方法的有效实例,因为 MyClassT 派生自 MyClass
和 MyTemplateData
。为了能够通过保持所需的行为来编译此 class,我应该更改什么?
有一个解决方案,但让我解释一下为什么我不想跟随:
template <typename T>
class MyClassT : public MyClass, public T {
void Method() {
T::Execute();
}
};
在上面的例子中,它确实可以编译,但是这假设我的 T
class 总是实现 Method
。然而,我想要的是在 MyClassT
中有一个空的 Method
而不是如果 T
class 实现它应该被 T::Method
覆盖。
所以下面也会编译,但是 T::Method
永远不会被调用。
template <typename T>
class MyClassT : public MyClass, public T {
void Method() {
}
};
MyClass* Interface = new MyClassT<MyTemplateData>();
Interface->Method(); // <- here, MyTemplateData::Method() gets never called
是否有解决方案,或者这是编译器特定的错误?
编辑:再举一个例子,这次编译器应该确切地知道派生的class实现了所需的抽象方法。
struct InterfaceA {
virtual void MethodA() = 0;
};
struct InterfaceAB : InterfaceA {
virtual void MethodB() = 0;
};
class ImplA : public virtual InterfaceA {
public:
void MethodA() {
}
};
class ImplAB : public virtual InterfaceAB, public ImplA {
void MethodB() {
}
};
编译器希望 class ImplAB
实现 MethodA
,因为 InterfaceAB
需要同时实现 MethodA
和 MethodB
。但是,由于 ImplAB
也派生自 ImplA
,并且编译器确切地知道 ImplA
实现了该接口,所以根本没有问题。
至少应该有一个关键字来提示编译器在哪里实现了抽象方法,如下例所示。我看不出有任何理由至少在以任何方式使用 VTABLE 时应该避免这种情况。
class ImplABCD : public implements InterfaceABCD, public ImplA, public ImplB, public ImplC, public ImplC {
};
此 implements 关键字表示 InterfaceABCD
中的所有抽象方法将在 ImplABCD
中实现,或者已经在一个中实现继承的 classes。如果存在签名歧义,那么当从具有相同基础 classes.
如果您不想使用我们的@ForEveR 解决方案,那么您可以执行以下操作。
使用 SFINAE std::enable_if。 内容如下:
template <typename T>
struct MyClassT : public MyClass, public T
{
typename std::enable_if<has_func_method<T>::value, void>::type Method()
{
T::Method();
}
typename std::enable_if<!has_func_method<T>::value, void>::type Method()
{
//do nothing
}
};
编辑: 您可以在 class 本身而不是内部使用 SFINAE。 那样可能会更好。 这是一个工作示例:
// Example program
#include <iostream>
#include <string>
struct MyClass
{
virtual void Method() = 0;
};
template< typename C, typename = void >
struct has_func_method
: std::false_type
{};
template< typename C>
struct has_func_method< C, typename std::enable_if<
std::is_same<
decltype( std::declval<C>().Method()),
void
>::value
>::type >
: std::true_type
{};
template <typename T, bool has_method = has_func_method<T>::value>
struct MyClassT : public MyClass, public T
{
};
template <typename T>
struct MyClassT<T, false> : public MyClass, public T
{
void Method()
{
//do nothing
std::cout << "doing nothing" << std::endl;
}
};
template <typename T>
struct MyClassT<T, true> : public MyClass, public T
{
void Method()
{
std::cout << "calling T::Method" << std::endl;
T::Method();
}
};
struct DataWithoutMethod
{
};
struct DataWithMethod
{
void Method()
{
std::cout << "method called" << std::endl;
}
};
int main()
{
MyClassT<DataWithoutMethod> data;
data.Method();
MyClassT<DataWithMethod> data2;
data2.Method();
}
I want to know if it is standard compliant behaviour
是的。
if the compiler thinks that the instance of the template class is still abstract
是。
even if the class to derive from implements this abstract methods.
没有。
你没有覆盖MyClass::Method
; T::Method
无关。
让我们暂时摆脱模板,因为它们无关紧要并且会分散我们对问题的注意力:
class MyClass {
virtual void Method() = 0;
};
class MyTemplateData {
void Method() {
}
};
class MyClassT : public MyClass, public MyTemplateData {
};
这是继承树:
MyClass MyTemplateData
\ /
\ /
MyClassT
Method
必须在左侧实现;也就是说,在 MyClass
、MyClassT
、 或介于两者之间的 。右边是一个不相关的继承分支:当你创建MyTemplateData::Method
时,这并没有覆盖MyClass::Method
,因为MyTemplateData
和MyClass
之间没有继承关系。
覆盖仅适用于直线。