views:

836

answers:

4

Is it possible to forward declare a class that uses default arguments without specifying or knowing those arguments?

For example, I would like to declare a boost::ptr_list< TYPE > in a Traits class without dragging the entire Boost library into every file that includes the traits. I would like to declare namespace boost { template<class T> class ptr_list< T >; }, but that doesn't work because it doesn't exactly match the true class declaration:

template < class T,
    class CloneAllocator = heap_clone_allocator,
    class Allocator = std::allocator<void*>
    >
class ptr_list { ... };

Are my options only to live with it or to specify boost::ptr_list< TYPE, boost::heap_clone_allocator, std::allocator<void*> in my traits class? (If I use the latter, I'll also have to forward declare boost::heap_clone_allocator and include <memory>, I suppose.)

I've looked through Stroustrup's book, SO, and the rest of the internet and haven't found a solution. Usually people are concerned about not including STL, and the solution is "just include the STL headers." However, Boost is a much more massive and compiler-intensive library, so I'd prefer to leave it out unless I absolutely have to.

+1  A: 

Any compilation unit that uses your facility that forward-declares boost stuff will need to include the boost headers anyway, except in the case that you have certain programs that won't actually use the boost part of your facility.

It's true that by forward-declaring, you can avoid including the boost headers for such programs. But you'll have to manually include the boost headers (or have an #ifdef) for those programs that actually use the boost part.

Keep in mind that more default template parameters could be added in a future Boost release. I'd advise against this route. What I would consider, if your goal is to speed compile times, is to use a #define to indicate whether the code using that boost library should be disabled. This way you avoid the forward declaration hassle.

wrang-wrang
Well, there are indeed parts of my code that don't use the Boost libraries but do include the traits file -- that's why this question isn't entirely futile. And yes, I am aware of the potential problem with changing template parameters. That's why I didn't like either choice.
Seth Johnson
Ok. So you use the forward declaration, and then your compilation units that DO use the boost library are responsible for including it on its own (where normally your header would include the boost headers it uses). Either way is annoying, but acceptable. Obviously you could split the traits class into two different-named different-header parts (even having the boost-using one inherit from the boost-free one) but that seems gross and would make specializing even more annoying.
wrang-wrang
+1  A: 

I don't think you can forward declare a template with default arguments unless the library in question provided its own forward declaration header. This is because you can't respecify the default arguments (even if they match... gcc will still report “error: redefinition of default argument”).

So to the best of my knowledge the solution is for the library to supply a forward declaration header Foo_fwd.h:

#ifndef INCLUDED_Foo_fwd_h_
#define INCLUDED_Foo_fwd_h_
template<class T, class U=char> class Foo; // default U=char up here
#endif

and then the full implementation in Foo.h would be:

#ifndef INCLUDED_Foo_h_
#define INCLUDED_Foo_h_
#include "Foo_fwd.h"
template<class T, class U> class Foo { /*...*/ }; // note no U=char here
#endif

So now your code could use Foo_fwd.h as well... but unfortunately, since this approach requires modifying the original Foo.h to remove the default arguments this doesn't scale to 3rd party libraries. Maybe we should lobby the C++0x crew to allow equivalent respecification of default template arguments, à la typedefs...?

Ethan
A: 

Well same issue here. But with STL.

If one of my header use eg. std::vector then I have to include the entire header. From this time every time I include my header even if my source code does not refer to std::vector at all the header gets included along with my header. If you include this header in lots of place that will mean lots of overparsing.

So I forward declared the std::vector and used std::vector* 's but my code don't want to compile due to the default arguments. If I place the default arguments in my header then the compiler refuses to compile the stl header due to the default argument respectification.

What I'm trying to do in this situation is creating my own Vector class that adapts the std::vector and forwards every method call to it. Probably this could solve the problem.

Calmarius
In the case you describe, all that indirection DEFINITELY isn't worth the effort. The only penalty in your case is the (very small -- time gcc's compile with and without `#include <vector>`) compile-time penalty of loading a couple of files. Using a pointer to a vector, or adding a wrapper class, will complicate your life, add possible run-time penalties, and won't even solve the problem because of the way templates work.
Seth Johnson
I haven't write any code yet about that and it seems I shouldn't...It seems I need to include the header anyway into my headers. I read in an other thread that really big c++ projects are compiling for 30-60 minutes average after every compiling optimalization due to the preprocessor and template thing. It's a failure of C++ isn't it?
Calmarius
A: 

Yes. Default template arguments may be specified any time, anywhere, so long as the declarations don't conflict with each other. They are ultimately merged together from the various declarations.

Even this is legal:

template< class A, class B, class C = long >
class X;

template< class A, class B = int, class C >
class X;

template< class A = short, class B, class C >
class X { };

A similar example is given in §14.1/10. According to that paragraph, function default arguments behave similarly.

Good luck on getting the forward declaration to behave itself and not barf on everything!

Potatoswatter