0

I have the following class structure.

class Base {
  protected:
    template <typename Type>
    Type convert(); // no implementation

  public:
    template <typename Type>
    operator Type() { 
      Type t(convert<Type>());
      // log conversion
      return t;
    }
  }
};

class D1: public Base
{
   protected:
     template <type Type>
     Type convert() {
       Type t;
       // Set t
       return t;
     }
 };

 template <typename Type>
 class D2: public Base
 {
    public:
      D2(Type v): value(v) {}

    protected:
      template <typename Type2>
      Type2 convert() {
          return value;
      }

    private:
      Type value;

 }

I am looking for a non-templated class that can be converted into a different type by a type conversion. I do not want to make Base a templated class, as that complicates how it is used.

I would like to force D2 to only allow Type2 and Type to be the same class. As written, if Type can be cast or converted to Type2, the code will work. I am being confused as to whether convert should be declared virtual (the answer appears to be no) and partial nested specializations.

I would like to provide a single specialization in D2 of

template <typename Type>
template <>
Type D2<Type>::convert<Type>() { ... }

but it is clear that this is not allowed.

I also tried

template <>
template <typename Type>
Type D2<Type>::convert<Type>() { ... }

which complained that "prototype for ‘Type D2::convert() const’ does not match any in class ‘D2’

I can't remove the template on convert because it is needed by D1.

Is there a way to get this to work?

At a high level, what I need is to be able to do the following:

Base *b = new D1(/*stuff*/);
int i = *b; // calls D1::convert<int>                  (a)
b = new D1(/*different stuff*/);
std::string s = *b; // calls D1::convert<std::string>  (b)
b = new D2<int>(/* other stuff */);
int j = *b; // calls D2<int>::convert<int>             (c)
b = new D2<std::string>(/*still other args*/);
s = *b; // calls D2<std::string>::convert<std::string> (d)

// This should fail as a compile time or link time error
b = new D2<int>(/*... */);
std::string s_fail = *b; // convertable to int, not string
   // tries to call D2<int>::convert<std::string>, which is either 
   // not implemented or has an implementation that fails to compile, 
   // probably by casting an int to string             (e)

The desired behavior is:

(a) Call a method in D1 that returns an int
(b) Call a method in D1 that returns a string
(c) Call a method in D2<int> that returns an int
(d) Call a method in D2<string> that returns a string
(e) Fail as early as possible, ideally at compile time or link time

Is that not possible in C++? I'm happy to rewrite everything except the public interface of Base, D1 not being templated and D2 being templated.

EDIT: Clarify the behavior that I want

Community
  • 1
  • 1
Troy Daniels
  • 3,270
  • 2
  • 25
  • 57
  • So, `D1` is convertible to `int` and `string`, but `D2`, being derived from `D1`, should only be convertible to `int`? That violates the LSP and since you use a pointer to the base class (`b`) this can only be detected at run-time. – dyp May 30 '14 at 22:13
  • 3
    You cannot have at compile time both an error and a success for `std::string s = *b` depending of the dynamic class of `b`. – Jarod42 May 30 '14 at 22:21
  • @dyp D2 is derived from Base, not D1. – Troy Daniels May 30 '14 at 22:31
  • @TroyDaniels Ah, ok. The problem remains kind of: you can call only function declared in `B` via a pointer to `B`. – dyp May 30 '14 at 22:36
  • @Jarod42 If I have specializations for D2::convert and D2::convert but not the mixed cases, what happens when I try to link `std::string s = *b` and `b` was created by `b = new D2`? It seems that it should look for `Base::convert` and `D2::convert` and find neither, so linking would fail. – Troy Daniels May 30 '14 at 22:38
  • @dyp So I can use inheritance or templates here, but not both? – Troy Daniels May 30 '14 at 22:41
  • @TroyDaniels linking happens before runtime! – M.M May 30 '14 at 23:12
  • Hmm I'm not sure I understand what you mean with that. The problem is that the compiler can only see the declared type of `b`, not what object it is pointing to. Consider `B *b = rand() ? new D1 : new D2;`. So the `B` type, which I guess is the same as `Base`, provides the complete interface for `B*`s. Additionally, you [can't have virtual function templates](http://stackoverflow.com/q/2354210/420683). – dyp May 31 '14 at 12:09
  • e) is not possible at compile/link time. Consider: `B* b = rand() ? new D1(..) : new D2(..); std::string s_fail = *b;` – dyp Jun 11 '14 at 15:27
  • @dyp So that would be a runtime error? How would it not produce a compile/link time error if the implementation of D2::convert attempts to cast T2 to T1? – Troy Daniels Jun 11 '14 at 15:51
  • Also, (e) is a bug, so I don't really care what it does. What I want is for (a) through (d) to work as described. – Troy Daniels Jun 11 '14 at 15:58
  • If you want the behaviour of `x = *b` to change dependent on the (dynamic) type of the object `b` points to, you need to use virtual functions or a similar tool. Those require that the functions must be defined, since the compiler in general cannot know whether or not the function is actually called because it only sees one Translation Unit (source file) and the call might happen in another TU. So if you insert a compile-time error, it will *always* be an error whether or not the function is called. Similarly, if it's a link-time error (function called from a virtual function not defined). – dyp Jun 11 '14 at 16:04
  • a) thru d) -- just use virtual functions (a fixed set of conversions). Other solutions probably require circumventing the type system (something like a virtual factory pattern). – dyp Jun 11 '14 at 16:06
  • Am I correct in understanding that a method cannot be a template and virtual, so what I am trying to do is not possible. The example that I gave used string and int, but I also need to support double, bool and a variety of user-defined types. The "fixed set of conversions" is not bounded, which is why I am trying to use templates. (Each new type defines its own conversion.) – Troy Daniels Jun 11 '14 at 16:16
  • Correct: to quote myself from above "you can't have virtual function templates". But you can -- if you really must, I'd first reconsider the design -- circumvent the type system by using a factory called via a virtual function. I can give you an example, but it's not going to be very neat. – dyp Jun 11 '14 at 18:33

0 Answers0