【问题标题】:Class to take any subclass of Base类采用 Base 的任何子类
【发布时间】:2018-03-05 11:47:41
【问题描述】:

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

我有一个名为 Learner 的课程:

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

以及对象继承的基类。

我的想法是让 Learner 接受任何 Base 子类的对象,例如Base1、Base2 等。我可以使用 c++ 11 的 type_traits 断言类型。问题是,我需要为每个基本扩展提供模板专业化,例如:

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 个模板规范似乎不正确......它适用于少量扩展,但如果它只是难以处理,则适用于大量扩展。

【问题讨论】:

  • 与您的问题无关,但我对将运行时多态性(虚拟+继承)工具与静态多态性工具(模板)混合的想法有点困惑。不过,也许您有正当理由这样做!
  • @Caninonos - 可悲的是,我不知道这两者之间的区别。最有可能的是,我只是在尝试,就像我说的那样,我的 C++ 知识很少。
  • 每个派生类的虚方法都可以简单地用 override 来声明。
  • @FrancisCugler - 我当然是。事情是学习者的声明要求我为每个基类派生提供专业化声明。
  • @Horus 啊,由于您显示的代码量很少,所以不确定。至于 static_assert(...),这不是我的优势之一,至于部分专业化......它也让我变得更好。

标签: c++ templates


【解决方案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

【讨论】:

  • 首先感谢您的回复。但是,很可能是因为我没有正确表达自己,这不是问题所在。请务必在我和 FrancisCugler 之间查看 Op(我的)初始帖子中的 cmets。
【解决方案2】:

除了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&lt;T&gt; 中添加 2 个构造函数。一种用于接受参数以能够构造D3 对象,另一种用于接受D3 对象;现在我们可以轻松做到这一点:

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

我希望这可以帮助您理解类模板的专业化和/或部分专业化的推理。对于默认的可构造完整类型,您不需要专门化,但对于非默认类型,您需要这样做,否则它们被视为不完整类型。

根据您的代码库,您可能不必specialize 每个子类。这完全取决于它们的构造方式以及它们是否是完整的类型。

【讨论】:

【解决方案3】:
template <typename T>
class Learner 
{
    static_assert(std::is_base_v<Base, T>); 

    T* _someRef;
    // ...
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 2010-10-16
    相关资源
    最近更新 更多