pointer to subclass as template parameter

Started by
12 comments, last by Antheus 14 years ago
I have a template function (zug) that takes a pointer to a CFoo as its parameter. I have a global variable (bar) whose type (CBar) is derived from CFoo. Is it possible to invoke zug() with bar's address as the template parameter? I can't see any reason why not, but every method I try seems to fail.
struct CFoo {};
struct CBar : public CFoo {};

template
  <CFoo* P>
void zug() {}

CFoo foo;
CBar bar;
CFoo& bar_as_foo(bar);

CFoo* const bar_foo_ptr = &bar;
const CFoo* const c_bar_foo_ptr = &bar;

int main()
  {
    zug<&foo>(); // Works fine.
    zug<&bar>(); // 1. G++: error: no matching function for call to 'zug()'
    zug<(CFoo*)(&bar)>(); // 2. G++: error: 'bar' cannot appear in a constant-expression.  etc..
    zug<&bar_as_foo>(); // 3. G++: error: no matching function for call to 'zug()'
    zug<bar_foo_ptr>(); // 4. G++: error: 'bar_foo_ptr' cannot appear in a constant-expression
    zug<c_bar_foo_ptr>(); // 5. G++: error: 'c_bar_foo_ptr' cannot appear in a constant-expression.
    
    zug<(CFoo*)(&bar_as_foo)>(); // 6. G++: error: 'bar_as_foo' cannot appear in a constant-expression.  etc...
    zug<bar>(); // 7. G++: error: 'bar' is not a valid template argument because 'bar' is a variable, not the address of a variable
    
    zug<&((CFoo&)bar)>(); // 8. G++: error: parse error in template argument list.
    ((CFoo&)bar) = foo; // Syntax works here though.
  }
Does anyone know how to do this, or if it's impossible? Thanks. (g++ 4.5.2 / ubuntu 64 bit)
Advertisement
Template parameters are compile time, pointers to instances are run time. The reason the first works it that the type is correct, but the address is not passed to the function and can not be used as is. You would need to change it to take an instance as a function parameter.
Quote:Original post by CmpDev
Template parameters are compile time, pointers to instances are run time. The reason the first works it that the type is correct, but the address is not passed to the function and can not be used as is.


The following shows zug() using the instance. The pointer to foo is constant, and thus available compile-time.

#include <iostream>struct CFoo  {    void say()      { std::cout << this << std::endl; }  };struct CBar : public CFoo {};template  <CFoo* P>void zug()  {    P->say();  }CFoo foo;int main()  {    zug<&foo>();    std::cout << "foo @ " << &foo << std::endl;  }


Compiles fine and outputs:
0x601180
foo @ 0x601180


In the original example, bar's address is also available at compile time. bar is a CBar, and CBar is derived from CFoo, so bar is a CFoo.

&bar is a CFoo* and is available at compile-time (just like &foo). Yet &foo works and &bar doesn't.
What are you really trying to do? And why?
Quote:What are you really trying to do? And why?


I'm resurrecting an old project of an allocator library. I'm just working on making the interface tidier.
The library guarantees a minimum space efficiency (committed memory is kept under 2*(requested memory) + ~10MB), yet allocation operations are O(ln N) so there aren't any freezes due to compacting memory.
To do this the allocator must be given autonomy and allowed to move allocated chunks at will, so the chunks contain a pointer to a move function that keeps links etc intact after moving.
Due to this extra type-specific information requirement, I can't just overload new and delete.

I have an allocator object class that provides the alloc() and dealloc() calls.

Like STL I want to allow the use of multiple allocators (here, two can operate safely in different threads without blocking eachother).

STL uses an allocator trait class as a (defaulted) template parameter in classes such as std::vector. But I think it'd be nicer to use vector<&allocator_pointer> than vector<trait_class> as you avoid having to write trait_class.

All works well for a single type of allocator class. However, I now want to make my allocator class abstract, mainly because there can be different patterns of allocation that can be modeled in different ways.

eg:
class CDerivedAllocator : public CAllocator  {    // ...  };CDerivedAllocator my_allocator;// Now for a class that uses my allocator:template <CAllocator* alloc>class CObject  {    // ...  };CObject<&my_allocator> object; // <- this is the problem.



I hope that makes sense.
It's not an insurmountable problem, but it would be an improvement, and I can't see why it's not possible.

I can point out where in the standard where it seems to disallow what you're trying to do (Section 14.3.2 paragraph 5, second bullet point), but I don't know why the rule exists.
Quote:Original post by SiCrane
I don't know why the rule exists.

C++ does not support contravariance.

Stephen M. Webb
Professional Free Software Developer

you have to make the destructor of base class virtual. otherwise no vtable is generated for casting.
Quote:Original post by Bregma
Quote:Original post by SiCrane
I don't know why the rule exists.

C++ does not support contravariance.


Explain?
Quote:Does anyone know how to do this, or if it's impossible?

Templates operate on types. A Java equivalent would be Class instance.

In addition, integral integer types may be used as parameters, and those that map directly to them (bool, enum).

I'm not sure of exact reasons, but the above are platform-independent. Pointers, doubles, references are not. Addresses might not even be unique - under DOS memory model different addresses might map to same physical memory.

Address is not known until link time.
int bar;int main(int argc, char* argv[]){	foo<(size_t)&bar>();}
The reason compiler barfs on this is that location, or even existence of bar is determined by the linker - compiler only emits a symbol, and template expansion is completed before then.

Templates should be thought of as macros, they are interpreted in a similar way. They have no knowledge of semantics, they work on opaque symbols. When done, the resulting code is passed to compiler which performs the optimizations and error checking. Historically, this caused code bloat, but compilers these days are smart enough to avoid it.

So the above is same as if asking pre-processor to do something like:
#define a &bar// if &bar is not null#IF a
It will be true, since a is defined as '&bar', but not because &bar is non-null. Pre-processor doesn't understand what &bar is, it treats it just as a bunch of characters.

This is also part of the reason why error messages from templates are such a complete mess.

This topic is closed to new replies.

Advertisement