views:

719

answers:

4

This code:

template <typename T>
struct A
{
     T t;

     void DoSomething()
     {
          t.SomeFunction();
     }
};

struct B
{
};

A<B> a;

is easily compiled without any complaints, as long as I never call a.DoSomething().

However, if I define DoSomething as a virtual function, I will get a compile error saying that B doesn't declare SomeFunction. I can somewhat see why it happens (DoSomething should now have an entry in the vtable), but I can't help feeling that it's not really obligated. Plus it sucks.

Is there any way to overcome this?

EDIT 2: Okay. I hope this time it makes sence: Let's say I am doing intrusive ref count, so all entities must inherit from base class Object. How can I suuport primitive types too? I can define:

template <typename T>
class Primitive : public Object
{
    T value;
public:
    Primitive(const T &value=T());

    operator T() const;

    Primitive<T> &operator =(const T &value);
    Primitive<T> &operator +=(const T &value);
    Primitive<T> &operator %=(const T &value);

    // And so on...
};

so I can use Primitive<int>, Primitive<char>... But how about Primitive<float>? It seems like a problem, because floats don't have a %= operator. But actually, it isn't, since I'll never call operator %= on Primitive<float>. That's one of the deliberate features of templates.

If, for some reason, I would define operator %= as virtual. Or, if i'll pre-export Primitive<float> from a dll to avoid link errors, the compiler will complain even if I never call operator %= on a Primitive<float>. If it would just have fill in a dummy value for operator %= in Primitive<float>'s vtable (that raises an exception?), everything would have been fine.

A: 
Jerry Coffin
I think you misunderstood the question. he wants to be able to NOT declare DoSomething if the function is never called.
shoosh
@shoosh, if i understood him correctly, he wants to declare it always, but he don't wan to get error messages if `T` doesn't have the function
Johannes Schaub - litb
I'm not sure you got the question. The problem is that the code WON'T compile, not that it will. The whole idea is that T might have a SomeFunction member, ot might not.
sold
Yup, you're right -- I didn't read his question carefully enough.
Jerry Coffin
A: 

One way to overcome this is to specialize A for template argument B and not declare DoSomething()

template <>
struct A<struct B>
{
     T t;

};

Ofcourse this means that you now have to implement the entire A struct from scratch.

shoosh
Not an option :P What if I have an infinitely large set of classes that support SomeFunction, and an infintely large set of classes that doesn't?
sold
Then your executable size would be infinite and not very useful.
shoosh
+2  A: 

Put the virtuals into selectable base classes...

struct Jumper
{
    virtual void Jump =0;
};

struct Crawler
{
    virtual void Crawl() =0;
};

struct JumperCrawler:
    public Jumper,
    public Crawler
{
};

template<typename T, typename Methods>
class ICanBoostJumpingAndCrawling :
    public Methods
{
    T t;
};

Now you can use ICanBoostJumpingAndCrawling with Jumper,Crawler or JumperCrawler supplied as the Methods template parameter; realizing that you need to be derived from it so that you can implement Jumping and or Crawling in a subclass.

FYI, This makes the name "ICanBoostJumpingAndCrawling" completely misleading because it may or may not be able to do that; which means it should be renamed to something like "Booster".

kdubb
Again, look at the question. The supplied code work OK. the problem is that if I define, say, ICanBoostJumpingAndCrawling::Jump as VIRTUAL, then the template machinery WOULD require me to define T::Jump() (which has nothing to do with the boosting). And that's what I am trying to solve.
sold
A: 

So the compiler should be able to work out within the compilation unit just what is in use. As soon as you start to involve multiple compilation units it no longer has the limited scope and takes the necessary step to ensure that all of the class can compile.

For exporting from a library don't force the pre export, you can ignore the warnings about not exporting the templates as long as you compile all the code with the same compiler the template will be compiled the same in all locations, only compile what is necessary within each compilation unit.

To work around the problem with virtual, then the best you can do is to defer the issue to some other class - don't put virtual in the template.

Perhaps

  • addition of a 'traits' portion to your template would allow a cheap way out.
  • use multiple inheritance to define the composite template, like using shims

ie.

template <typename T>
class Additive
{
public:
    Primitive<T> &operator =(const T &value);
    Primitive<T> &operator +=(const T &value);
};

template <typename T>
class Multiplicative
{
public:
    Primitive<T> &operator *=(const T &value);
    Primitive<T> &operator /=(const T &value);
};

template <typename T>
class Integers : public Additive<T>, public Multiplicative<T>;

I'd really go back and ask if your abstracting the right level of information for making the template.

Greg Domjan