2

I reckon this is a simple question, but so is my C++ knowledge.

I have a class named Learner:

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

And a base class from which objects inherit.

My idea is to have the Learner accept any objects which subclass Base, e.g. Base1, Base2, etc. I can assert the type using c++ 11's type_traits. Problem is, I am required to provide template specialization for each of the Base extensions, e.g.:

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(...);

Which partly defeats the purpose of what I'm trying to achieve, as the operations will then be the same for every class since well, they all implement Base's virtual functions. How can I achieve this? Making n template specifications doesn't seem right... It works fine for a small number of extensions, but for larger numbers if its just intractable.

HGSF
  • 57
  • 5
  • Unrelated to your question, but I'm a bit perplexed by the idea of mixing tools for runtime polymorphism (virtual+inheritance) with tools for static polymorphism (templates). Maybe you have a legitimate reason to do so though! – Caninonos Mar 05 '18 at 11:55
  • @Caninonos - Sad part is, I have no idea about the difference between those two. Most likely, I'm just winging it, as like I said, my C++ knowledge is scarce. – HGSF Mar 05 '18 at 12:02
  • Each of your derived class's virtual methods you can simply declare them with override. – Francis Cugler Mar 05 '18 at 12:22
  • @FrancisCugler - I am of course. Thing is Learner's declaration requires me to provide specialization declaration for each of the Base class derivations. – HGSF Mar 05 '18 at 12:27
  • @Horus ah okay wasn't sure due to the minimal amount of code you shown. As for static_assert(...), that is not one of my finer strengths, and as for partial specialization ... it gets the better of me too. – Francis Cugler Mar 05 '18 at 12:29
  • @FrancisCugler - I updated the post, seems to reflect a bit more of my questions now. I apologize if I wasn't clear (perhaps I still ain't ?!). See the template declarations of the Learner constructors. I provide a generic-templated constructor and its implementation, but I am required to provide individual templates for each Base extension (of course they will call the default constructor, so this makes 0 sense to me), why use them at all?! – HGSF Mar 05 '18 at 12:31
  • @Horus that's a very good question... – Francis Cugler Mar 05 '18 at 12:34
  • @FrancisCugler - Oh, so there is no alternative? It completely defeats the purpose of templating. – HGSF Mar 05 '18 at 12:36
  • @Horus no; templates are very powerful and versatile, it's just that their syntax is a pain in the ...; A function template reduces writing multiple functions that do the same task for different types. A class template is similar in regards. Specialization is needed when the actual implementation is different for each type or in your case, class. – Francis Cugler Mar 05 '18 at 12:38
  • @FrancisCugler - Oh no don't take me wrong, I just meant that for what I'm trying to achieve it is a big useless... Finally found something in C++ I can't do. Thank you so much for your help and time. – HGSF Mar 05 '18 at 12:45
  • I noticed something with your class template declaration; you have `template class` I think you meant to have `template `. – Francis Cugler Mar 05 '18 at 12:49
  • @FrancisCugler - You are absolutely right. I made a mistake as I hand-typed it just for exposing the problem here. Again, thank you. – HGSF Mar 05 '18 at 12:55
  • @Horus I edited my original answer and shown a simple break down of the template class with default constructors without specialization but when the template accepts a class that is not default constructible you have to specialize it. – Francis Cugler Mar 05 '18 at 13:23
  • I now suspect that you put the implementation in a .cpp file and run into this problem: [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Bo Persson Mar 05 '18 at 13:24
  • @BoPersson - That is exactly what I needed! Although I couldn't get the .tpp to work, as it kept giving me a C9995 error which I couldn't fix, merging the declarations to the end of the header did the trick (I think its rather ugly this way but, it works I guess). Thanks! – HGSF Mar 06 '18 at 22:34

3 Answers3

1

If I understand your question, I think what you're looking for is static_assert:

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

For C++11, you're required to provide a message:

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

Also, you have more options on compile time checks, if need be, using type_traits:

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

solstice333
  • 3,399
  • 1
  • 31
  • 28
  • First of all, thank you for your reply. However, and most likely because I did not express myself correctly, this is not the question. Please do see the comments on Op's (mine) initial post, between myself and FrancisCugler. – HGSF Mar 05 '18 at 12:36
1

Apart from the static_assert(...) to help fix the problem with the virtual inheritance you can use the keyword override in the derived classes as such:

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

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

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

This way it should help to avoid any ambiguity.

Edit

I'm adding this section to see if it helps the 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;
}

Here I didn't have to use any specializations. Now this pattern will probably only work with complete types as well as those that are default constructible. If they are not complete or default constructible that is where you will probably need to use specialization. For example:

Now let's see where specialization comes into play let's add a 3rd class with a non default constructor.

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;
    }
};

If we were to try and do this:

{
    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
}

None of the above will work. So we HAVE to specialize as such:

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();
    }
};

With this specialization I chose to add 2 constructors to this version of Learner<T>. One for accepting a parameter to be able to construct a D3 object and one that takes a D3 object; now we can easily do this:

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

I hope this helps you to understand the reasoning of specializations and or partial specializations with class templates. With default constructible complete types you don't need specializations but with non default types you do other wise they are considered incomplete types.

Depending on your code base you may not have to specialize every sub class. It all depends on how they are constructed and if they are complete types.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • Thanks for all your help. The answer to my question can be found here: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file – HGSF Mar 07 '18 at 09:51
0
template <typename T>
class Learner 
{
    static_assert(std::is_base_v<Base, T>); 

    T* _someRef;
    // ...
};
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416