views:

428

answers:

5

I have a template class defined like so:

template <class T>
class Command {
public:
    virtual T HandleSuccess(std::string response) = 0;
    virtual std::string FullCommand() const = 0;
    // ... other methods here ...
};

Will C++ allow me to create a non-template subclass of a template class? What I mean is can I do something like this:

class NoopCommand : public Command<NoopResult> {
public:
    NoopResult HandleSuccess(std::string response);
    std::string FullCommand() const;
    // ... other methods here ...
};

As is that is not working for me because it says the following virtual functions are undefined:

T admix::Command<T>::HandleSuccess(std::string) [with T = admix::NoopResult]
std::string admix::Command<T>::FullCommand() const [with T = admix::NoopResult]

How can I specifically define them for the given T?

+2  A: 

The pattern you use is widely known as the "Curiously Recurring Template Pattern". so, yes, you can do that.
I can't think of a reason why it does not compile.

Oren S
Actually that mistake was just when typing up the question, it has the argument in the code.
Matt Moriarity
Does it matter that the the function definitions are in the .cpp file and not the .h?
Matt Moriarity
A: 

The code you gave compiles for me, without errors (after adding a struct NoopResult { };). Maybe there's a problem in the code you left out?

bdonlan
Did you include some code that tries to instantiate the class? The class is considered abstract by the compiler.
Matt Moriarity
Yes. There may be link time issues if the bodies of `HandleSuccess` and `FullCommand` are missing of course - I assumed your error was from compile time, but since you accepted the other answer I guess they were from the linker?
bdonlan
+4  A: 
Johannes Schaub - litb
I gave default versions of both methods, but now I have a problem. The code is calling the default versions instead of the subclass' overridden versions, which means it doesn't consider my `NoopResult HandleSuccess(std::string response)` in the subclass to override the superclass.
Matt Moriarity
As we figured out in IRC, that was because you have 1) Made your functions non-pure 2) sliced the derived object part. So the base class functions were called because the object wasn't a complete derived object anymore.
Johannes Schaub - litb
How is this relevant? Yes, per 14.7.1/9 the compiler is free to instantiate `Command<T>::HandleSuccess(std::string)`. But it's just a declaration that would be instantiated. Since it's a pure virtual declaration the subsequent lack of a definition is not an error.
MSalters
The declaration is instantiated anyway (the declaration of any function is implicitly instantiated when its class is implicitly instantiated). But for a virtual function (and a pure one is also virtual), the function itself can be instantiated too. It's not immediately an error if the template definition is not available, because the definition could be exported. But if it isn't, then it's an error.
Johannes Schaub - litb
"Phases of translation", 2.1/1b8 says "Each translated translation unit is examined to produce a list of required instantiations. The definitions of the required templates are located. The program is ill-formed if any instantiation fails.". At least that's how i'm interpreting the matter.
Johannes Schaub - litb
+3  A: 

"virtual functions are undefined" means you have not defined the function bodies of NoopCommand::HandleSuccess and NoopCommand::FullCommand.

The following should solve your problem.

class NoopCommand : public Command<NoopResult> {
public:
    NoopResult HandleSuccess(std::string response) {}
    std::string FullCommand() const {}
    // ... other methods here ...
};

Or you you have a NoopCommand.cpp, make sure it's included in your build process.

lyxera
I have defined them. I also defined superclass default versions instead of making them pure virtual. However, my default versions are being called instead of the overridden definitions in the subclass.
Matt Moriarity
What is the definition of your NoopResult HandleSuccess(std::string response);andstd::string FullCommand() const;and how are you invoking them?
lyxera
A: 

litb found the solution on ##c++ last night.

The issue was I was passing a NoopCommand to a function like this:

void SendCommand(Command<T> command);

When I should have made the signature this:

void SendCommand(Command<T>& command);

Making that change allows everything to compile.

Matt Moriarity