无法实例化抽象 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 中,您声明了一个纯虚方法,该方法采用 constT 的引用。但所有引用本质上都是 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 TINconst TOUT& 有不同的行为,即使您认为它们应该相同。

一个简单的解决方法是将 const 添加到 IFoo 实例化中的值类型:

struct Bar : IFoo<const TVALUE &, TVALUE, TINDEX>
//          here  ^^^^