用于获取Base的任何子类的类

时间:2018-03-05 11:47:41

标签: c++ templates

我认为这是一个简单的问题,但我的C ++知识也是如此。

我有一个名为Learner的课程:

template <class T>
class Learner {
 T* _someRef;
 (...)
}

对象继承的基类。

我的想法是让学习者接受任何子类Base的对象,例如Base1,Base2等我可以使用c ++ 11&gt; type_traits来断言类型。问题是,我需要为每个Base扩展提供模板特化,例如:

template Learner<T>::Learner(...) { //implementation };
// However I am required to declare template-specifications for each Base
// derived type, else I'll get compiler errors. This seems useless as
// the generic-inplementation will still be called, but requires me to
// specify a declaration for each Base extension.What if I have n Base
// extensions, makes no sense to do this for 50 Base extensions.
template Learner<Base1>::Learner(...);
template Learner<Base2>::Learner(...);

这部分地违背了我试图实现的目的,因为每个类的操作都是相同的,因为它们都实现了Base的虚函数。我怎样才能做到这一点?制作n个模板规格看起来并不正确...它适用于少量扩展,但对于较大的数字,如果它只是难以处理。

3 个答案:

答案 0 :(得分:1)

如果我理解了您的问题,我认为您正在寻找的是static_assert

template<typename T>
struct Foo {
   static_assert(is_base_of<Base, T>::value, "T must inherit from Base");
};

对于C ++ 11,您需要提供一条消息:

http://en.cppreference.com/w/cpp/language/static_assert

此外,如果需要,您可以使用type_traits:

在编译时间检查上有更多选项

http://en.cppreference.com/w/cpp/language/static_assert

答案 1 :(得分:1)

除了帮助解决虚拟继承问题的static_assert(...)之外,你可以在派生类中使用关键字override

class Base {
public:
    virtual doSomething() = 0; // purely virtual
};

class DerivedA : public Base {
public:
    virtual doSomething() override;
};

class DerivedB : public Base {
public:
    virtual doSomething() override;
};

这样可以避免任何歧义。

修改

我正在添加此部分以查看它是否有助于OP:

template <class T>
class Learner {
    T* _someRef;

public:
    Learner() { _someRef = new T(); }

    ~Learner() {
        if ( _someRef != nullptr ) {
           delete _someRef;
           _someRef = nullptr;
        }
    }

    void caller() {
        _someRef->doSomething();
    }
};

class Base {
public:
    virtual void doSomething() = 0;
};

class D1 : public Base {
public:
    virtual void doSomething() override {
        std::cout << "D1's doSomething was called" << std::endl;
    }
};

class D2 : public Base {
public:
    virtual void doSomething() override {
        std::cout << "D2's doSomething was called" << std::endl;
    }
};

int main() {
    Learner<D1> ld1;
    Learner<D2> ld2;
    ld1.caller();
    ld2.caller();

    _getch();
    return 0;
}

在这里,我没有必要使用任何专业。现在,此模式可能仅适用于complete类型以及default可构造的类型。如果它们不完整或默认可构造,那么您可能需要使用专业化。例如:

现在让我们看看专业化在哪里发挥作用让我们添加一个非默认构造函数的第3类。

class D3 : public Base {
private:
    unsigned int _x;
public:
    explicit D3( unsigned int x ) : _x( x ) {}

    virtual void doSomething() override {
        std::cout << "D3's doSomething was called: constructed with " << _x << std::endl;
    }
};

如果我们尝试这样做:

{
    Learner<D3> ld3; // compiler error
    Learner<D3> ld3( 5 ); // compiler error
    Learner<D3> ld3( D3( 5 ) ); // compiler error
    D3 d3(5);
    Learnder<D3> ld3( d3 ); // compiler error
}

以上都不会奏效。所以我们必须这样专业化:

template<>
class Learner<D3> {
private:
    D3* _d3;
public:
    explicit Learner( unsigned int x ) {
        _d3 = new D3( x );
    }

    explicit Learner( D3 d3 ) {
        _d3 = new D3( d3 );
    }

    ~Learner() {
        if ( _d3 != nullptr ) {
            delete _d3;
            _d3 = nullptr;
        }
    }

    void caller() {
        _d3->doSomething();
    }
};

通过这种专业化,我选择在这个版本的Learner<T>中添加2个构造函数。一个用于接受能够构造D3对象的参数和一个接受D3对象的参数;现在我们可以轻松地做到这一点:

{    
Learner<D3> ld3a( 7 );
ld3a.caller();
D3 d3( 5 );
Learner<D3> ld3b( d3 );
ld3b.caller();
}

我希望这可以帮助您理解专业化的推理和/或使用类模板的部分专业化。使用默认的可构造完整类型,您不需要专门化,但使用非默认类型,您可以将其他类型视为不完整类型。

根据您的代码库,您可能不必每个子类specialize。这一切都取决于它们是如何构建的以及它们是否完整类型。

答案 2 :(得分:0)

template <typename T>
class Learner 
{
    static_assert(std::is_base_v<Base, T>); 

    T* _someRef;
    // ...
};