无法实例化抽象 class:为什么模板参数(引用)会导致此问题?
Cannot instantiate abstract class: Why is the template parameter (reference) causing this?
我在使用某些代码时遇到问题。
'Bar' : cannot instantiate abstract class
我(终于)能够用少量代码重现错误。
struct SomeStruct
{
// ******
};
template <typename TIN, typename TOUT, typename TINDEX>
struct IFoo
{
public:
virtual void add(const TIN item) = 0; // <-- BAD
//virtual void add(const TOUT& item) = 0; // <-- GOOD
// ******
};
template <typename TVALUE, typename TINDEX>
struct Bar : IFoo<TVALUE &, TVALUE, TINDEX>
{
public:
void add(const TVALUE& item)
{
// ******
}
// ******
};
int main(int argc, char *argv[])
{
SomeStruct someStruct;
Bar<SomeStruct, int> bar = Bar<SomeStruct, int>();
bar.add(someStruct);
// ******
}
谁能告诉我为什么使用带有模板参数的引用会导致此问题?
您的问题可以追溯到函数签名等基本概念:函数 signature/prototype 由函数名称、参数数量、参数数据类型和这些参数出现的顺序给出。
对于任何两个函数,如果上述任何一个不同,那么您正在处理两个不同的函数。
更准确地说,这两个代表两个不同的签名:
virtual void add(const TIN item) = 0;
virtual void add(const TOUT& item) = 0;
由于您在派生的 class 中仅实现了第二个,因此您会收到错误。
我们可以将您的示例进一步简化为:
template <typename T>
struct IFoo {
virtual void add(const T item) = 0;
};
template <typename T>
struct Bar : IFoo<T&> {
void add(const T& item) { }
};
在 Bar
中,您将 item
作为对 const T
的引用。在 IFoo
中,您声明了一个纯虚方法,该方法采用 const
对 T
的引用。但所有引用本质上都是 const
,所以这是多余的 - 它相当于只引用 T
.
对于 Bar<int>
- IFoo::add()
的签名是 void add(int& )
而 Bar::add()
是 void add(const int& )
。这些签名不匹配 - 因此 Bar
仍然是一个抽象 class。只是一个隐藏 IFoo::add()
。
如果您有 C++11 编译器,您应该更喜欢将 override
关键字添加到 Bar::add()
,这样您会得到一个编译器错误:
main.cpp:15:10: error: 'void Bar<T>::add(const T&) [with T = int]' marked 'override', but does not override
void add(const T& ) override { }
^
这里的问题是,当您编写 const TIN
并且 TIN
是引用类型时,const
适用于 reference 并且不是值类型。
这就是为什么您看到 const TIN
和 const TOUT&
有不同的行为,即使您认为它们应该相同。
一个简单的解决方法是将 const
添加到 IFoo
实例化中的值类型:
struct Bar : IFoo<const TVALUE &, TVALUE, TINDEX>
// here ^^^^
我在使用某些代码时遇到问题。
'Bar' : cannot instantiate abstract class
我(终于)能够用少量代码重现错误。
struct SomeStruct
{
// ******
};
template <typename TIN, typename TOUT, typename TINDEX>
struct IFoo
{
public:
virtual void add(const TIN item) = 0; // <-- BAD
//virtual void add(const TOUT& item) = 0; // <-- GOOD
// ******
};
template <typename TVALUE, typename TINDEX>
struct Bar : IFoo<TVALUE &, TVALUE, TINDEX>
{
public:
void add(const TVALUE& item)
{
// ******
}
// ******
};
int main(int argc, char *argv[])
{
SomeStruct someStruct;
Bar<SomeStruct, int> bar = Bar<SomeStruct, int>();
bar.add(someStruct);
// ******
}
谁能告诉我为什么使用带有模板参数的引用会导致此问题?
您的问题可以追溯到函数签名等基本概念:函数 signature/prototype 由函数名称、参数数量、参数数据类型和这些参数出现的顺序给出。
对于任何两个函数,如果上述任何一个不同,那么您正在处理两个不同的函数。
更准确地说,这两个代表两个不同的签名:
virtual void add(const TIN item) = 0;
virtual void add(const TOUT& item) = 0;
由于您在派生的 class 中仅实现了第二个,因此您会收到错误。
我们可以将您的示例进一步简化为:
template <typename T>
struct IFoo {
virtual void add(const T item) = 0;
};
template <typename T>
struct Bar : IFoo<T&> {
void add(const T& item) { }
};
在 Bar
中,您将 item
作为对 const T
的引用。在 IFoo
中,您声明了一个纯虚方法,该方法采用 const
对 T
的引用。但所有引用本质上都是 const
,所以这是多余的 - 它相当于只引用 T
.
对于 Bar<int>
- IFoo::add()
的签名是 void add(int& )
而 Bar::add()
是 void add(const int& )
。这些签名不匹配 - 因此 Bar
仍然是一个抽象 class。只是一个隐藏 IFoo::add()
。
如果您有 C++11 编译器,您应该更喜欢将 override
关键字添加到 Bar::add()
,这样您会得到一个编译器错误:
main.cpp:15:10: error: 'void Bar<T>::add(const T&) [with T = int]' marked 'override', but does not override
void add(const T& ) override { }
^
这里的问题是,当您编写 const TIN
并且 TIN
是引用类型时,const
适用于 reference 并且不是值类型。
这就是为什么您看到 const TIN
和 const TOUT&
有不同的行为,即使您认为它们应该相同。
一个简单的解决方法是将 const
添加到 IFoo
实例化中的值类型:
struct Bar : IFoo<const TVALUE &, TVALUE, TINDEX>
// here ^^^^