简体   繁体   中英

Inheriting a template class in C++ with unsupported type

I have a template Base class which I want to inherit. The Base class has a public method which makes an assumption about the template type. A simplified version of the code is presented below:

#include <iostream>

using namespace std;

template <typename V>
class Base {
 public:
  virtual void print() {
    a = "a";
    cout << "Base class" ;
  }
  void callIt() {
    print();
  }
  V a;
};

class Derived : public Base<int> {
 public:
  void print() override {
    cout << "Derived class\n";
  }
};


int main() {
  Derived d;
  d.callIt();
  return 0;
}

Compiling this code with GCC 6.2.0 gives me this error:

test.cpp: In instantiation of 'void Base<V>::print() [with V = int]':
test.cpp:13:10:   required from 'void Base<V>::callIt() [with V = int]'
test.cpp:28:12:   required from here
test.cpp:9:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
     a = "a";
     ~~^~~~~

I want to completely hide the Base class implementation of the print() method from the compiler. How can I do that?

EDIT: Ironically, commenting out the line a = "a"; allows the code to compile successfully and will print out Derived class .

It's possible to get your code working if you add a specialization for Base<int>::print() before declaring Derived :

#include <iostream>

using namespace std;

template <typename V>
class Base {
 public:
  virtual void print() {
    a = "a";
    cout << "Base class" ;
  }
  void callIt() {
    print();
  }
  V a;
};

template <>
void Base<int>::print() {
    cout << "Specialization\n";
}

class Derived : public Base<int> {
 public:
  void print() override {
    cout << "Derived class\n";
  }
};


int main() {
  Derived d;
  d.callIt();
  return 0;
}

Live Demo

Be careful doing this though: it breaks the assumptions the author of Base made about what types work with the template, and could easily lead to hard-to-trace bugs in library code.

Since your print is virtual, it has to be instantiated whenever you inherit from Base (because compiler needs to generate proper vtable) - even when the function is not called yet. It would not be the case if print was a non-virtual member - in this case it would be instantiated only when called.

The only solution I can think of is to see if you can get rid of virtual functions here - and if you can't, sorry, you are out of luck.

Simplifying to isolate the problem:

template <typename V>
class Base {
 public:
  virtual void print() {
    a = "a";
  }

  V a;
};

int main()
{
  Base<int> b;
}

results in:

<source>:5:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
a = "a";

This is because we are in effect saying:

int a = "a";

and of course that's an error because you can't assign a const char* to an int .

So the only reasonable conclusion to draw here is that Base<T> has been designed with the constraint that there must exist an accessible conversion from const char* to T .

If you want T to be anything else, then this base class is not suitable and you'll need to find another way to achieve what you want (encapsulation?).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM