Jon Purdy - 1 year ago 52

C++ Question

This is heavily simplified for the sake of the question. Say I have a hierarchy:

`struct Base {`

virtual int precision() const = 0;

};

template<int Precision>

struct Derived : public Base {

typedef Traits<Precision>::Type Type;

Derived(Type data) : value(data) {}

virtual int precision() const { return Precision; }

Type value;

};

I want a

`Base* function(const Base& a, const Base& b);`

Where the specific type of the result of the function is the same type as whichever of

`a`

`b`

`Precision`

`Base* function(const Base& a, const Base& b) {`

if (a.precision() > b.precision())

return new A( ((A&)a).value + A(b.value).value );

else if (a.precision() < b.precision())

return new B( B(((A&)a).value).value + ((B&)b).value );

else

return new A( ((A&)a).value + ((A&)b).value );

}

Where

`A`

`B`

`a`

`b`

`function`

`Derived`

`typeid()`

Answer Source

You can't have function() delegate to templated code directly without selecting between a massive list of all possible types, because templates are expanded at compile-time, and at compile-time function() does not know what derived types it will actually be called with. You need to have compiled instantiations of the templated code for every version of your templated `operation`

function that will be required, which is potentially an infinite set.

Following that logic, the only place that knows all of the templates that might be required is the `Derived`

class itself. Thus, your `Derived`

class should include a member:

```
Derived<Precision> *operation(Base& arg2) {
Derived<Precision> *ptr = new Derived<Precision>;
// ...
return ptr;
}
```

Then, you can define `function`

like so, and do the dispatching indirectly:

```
Base* function(const Base& a, const Base& b) {
if (a.precision() > b.precision())
return a.operation(b);
else
return b.operation(a);
}
```

Note that this is the simplified version; if your operation is not symmetric in its arguments, you'll need to define two versions of the member function -- one with `this`

in place of the first argument, and one with it in place of the second.

Also, this has ignored the fact that you need some way for `a.operation`

to get an appropriate form of `b.value`

without knowing the derived type of `b`

. You'll have to solve that one yourself -- note that it's (by the same logic as earlier) impossible to solve this by templating on the type of `b`

, because you're dispatching at runtime. The solution depends on exactly what types you've got, and whether there is some way for a type of higher precision to pull a value out of an equal-or-lower precision `Derived`

object without knowing the exact type of that object. This may not be possible, in which case you've got the long list of matching on type IDs.

You don't have to do that in a switch statement, though. You can give each `Derived`

type a set of member functions for up-casting to a function of greater precision. For example:

```
template<int i>
upCast<Derived<i> >() {
return /* upcasted value of this */
}
```

Then, your `operator`

member function can operate on `b.upcast<typeof(this)>`

, and will not have to explicitly do the casting to get a value of the type it needs. You may well have to explicitly instantiate some of these functions to get them to be compiled; I haven't done enough work with RTTI to say for sure.

Fundamentally, though, the issue is that if you've got N possible precisions, you've got N*N possible combinations, and each of these will in fact need to have separately-compiled code. If you can't use templates in your definition of `function`

, then you have to have compiled versions of all N*N of these possibilities, and somehow you have to tell the compiler to generate them all, and somehow you have to pick the right one to dispatch to at runtime. The trick of using a member function takes out one of those factors of N, but the other one remains, and there's no way to make it entirely generic.