views:

1158

answers:

12

I want to remove, if possible, the includes of both <vector> and <string> from my class header file. Both string and vector are return types of functions declared in the header file.

I was hoping I could do something like:

namespace std {
    template <class T>
    class vector;
}

And, declare the vector in the header and include it in the source file.

Is there a reference covering situations where you must include in the header, and situations where you can pull the includes into the source file?

+11  A: 

With a very few exceptions, you are not allowed to add things to the std:; namespace. For classes like vector and string, you therefore have no option but to #include the relevant Standard header files.

Also, notice that string is not a class, but a typedef for basic_string<char>.

anon
The std::vector is missing the allocator template parameter which is mandated by the standard.
Motti
You have no tasteful option but to include the standard header files, but there are distasteful options.
Thomas L Holaday
A forward declaration is not adding anything into the std namespace, only telling the compiler about something that is already there. Also, the rule is not so restrictive: you are allowed to add template specializations for your particular types inside the std namespace as long as they comply with the standard. "A program may add template specializations for any standard library template to namespace std. This results in undefined behavior unless the declaration depends on a user-defined name of external linkage and unless the specialization meets the requirements for the original template"
David Rodríguez - dribeas
To be honest, I wasn't clear what the user was trying to do with his example code. He certainly wasn't trying to add a new template specialisation, so I don't think your quote applies.
anon
@dribeas: the standard says you can't add declarations to namespace std (except permissible template specializations). If you haven't included any standard headers, then namespace std is empty. A declaration is adding to the namespace, not declaring "something that is already there". That said, it will work on most compilers. So will adding definitions to namespace std.
Steve Jessop
Interesting point 'onebyone' (for lack of a better way of addressing you). The question really is: is a forward declaration 'adding' something to the namespace? I believe not. You are telling the compiler that the forward declared symbol exists somewhere. Of course you can consider that you are injecting something into a previously empty (for the compiler in this compilation unit) namespace.
David Rodríguez - dribeas
+4  A: 

Standard containers often have additional default template parameters (allocators, etc.) so this will not work. For example, here's a snippet from GNU implementation:


  template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class vector : protected _Vector_base<_Tp, _Alloc>
    { ... };
Nikolai N Fetissov
+1, even if the example is bad. That implementation is exactly the standard definition (minimum requirement) for vector. The idea though is sound: the standard explicitly allows implementors to add extra template arguments as long as they have a default value.
David Rodríguez - dribeas
+4  A: 

This was something I was trying to do earlier too, but this is not possible due to templates.

Please see my question: http://stackoverflow.com/questions/389957/forward-declaration-of-a-base-class

As such headers don't change during your development it's not worth optimizing that anyway...

TomWij
Your question, and the answer had a important difference. You were intending to instantiate the std::string. In the question you posted you could not even use a forwarded class. Here the question deals with the situation where you could forward declare a regular class but you cannot with the STL due to its specific requirements.
David Rodríguez - dribeas
+6  A: 

This won't help for vector or string, but it might be worth mentioning that there is a forward reference header for iostream, called iosfwd.

Fred Larson
+1: very interesting! I didn't know that...
Paolo Tedesco
iosfwd is a delightful demonstration of forward declaration prowess.
Thomas L Holaday
+3  A: 

If string and vector are used only in signatures of non-public members of you class, you could use the PImpl idiom:

// MyClass.h
class MyClassImpl;
class MyClass{
public:
    MyClass();
    void MyMethod();
private:
    MyClassImpl* m_impl;
};

// MyClassImpl.h
#include <vector>
#include <string>
#include <MyClass.h>
class MyClassImpl{
public:
    MyClassImpl();
    void MyMethod();
protected:
    std::vector<std::string> StdMethod();
};

// MyClass.cpp
#include <MyClass.h>
#include <MyClassImpl.h>

void MyClass::MyMethod(){
    m_impl->MyMethod();
}

You are always including vector and string in the header file, but only in the implementation part of your class; files including only MyClass.h will not be pulling in string and vector.

Paolo Tedesco
This solves a particular scenario, but does not allow for many others. All your objects must be created dynamically, so you are now limiting the use of stack allocated elements. You must create a virtual destructor that will create a vtable even if your class did not require it. There will be a huge amount of boiler plate code just to define the whole interface in the base class and redefine and implement it on derived classes, and all those methods will have to be virtual. Using PIMPL is a little better solution to the non-problem (there is no problem in including <string> and <vector>.)
David Rodríguez - dribeas
Yes, you are right about the use of PImpl. I'll update the post.
Paolo Tedesco
+2  A: 

WARNING

Expect that doing this will cause uproar.

The language allows you to derive your own classes:

// MyKludges.h
#include <vector>
#include <string>
class KludgeIntVector : public std::vector<int> { 
  // ... 
};
class KludgeDoubleVector : public std::vector<double> {
  // ...
};

class KludgeString : public std::string {
  // ...
};

Change your functions to return KludgeString and KludgeIntVector. Since these are no longer templates, you can forward declare them in your header files, and include MyKludges.h in your implementation files.

Strictly speaking, derived classes do not inherit base class constructors, destructors, assignment operators, and friends. You will need to provide (trivial) implementations of any that you're using.


// LotsOfFunctions.h
// Look, no includes!  All forward declared!
class KludgeString;
// 10,000 functions that use neither strings nor vectors
// ...
void someFunction(KludgeString &);
// ... 
// Another 10,000 functions that use neither strings nor vectors

// someFunction.cpp
// Implement someFunction in its own compilation unit
// <string> and <vector> arrive on the next line
#include "MyKludges.h"
#include "LotsOfFunctions.h"
void someFunction(KludgeString &k) { k.clear(); }
Thomas L Holaday
It allows you to do that, but certainly doesn't encourage you to.
anon
the language allows you, but common sense advises against this.
xtofl
This helps the OP not one bit. He's trying to remove the #include of vector and string from the header. If he subclasses them, then uses them, he still has to include the headers. It's also generally a bad idea to subclass most of the STL - it wasn't really designed for that and you tend to create more problems than you solve.
Michael Kohne
You should never derive from a class that was not designed to be derived from. Doing so is opening the door to many (subtle and not so subtle) errors.
David Rodríguez - dribeas
He asked if it's possible, not if it's wise.
Thomas L Holaday
@Michael Kohne, he most emphatically does not need to include vector and string in the headers declaring his functions which return vectors and strings. He can forward declare his kludges there.
Thomas L Holaday
There, I added a bright warning for Neil Butterworth, xtofl, and dribeas and example code for Michael Kohne.
Thomas L Holaday
@dribeas, the Apache C++ guide has examples of how to derive from standard containers. http://stdcxx.apache.org/doc/stdlibug/16-2.html
Thomas L Holaday
Of course, Apache's best known product isn't written in C++. I don't see why you think they are a recognised authority on C++ best practice.
anon
Best practice != existing practice. I don't see why you think the standard containers were not designed to be base classes.
Thomas L Holaday
Because they don't have any virtual methods and thus cannot be treated polymorphically. And often, existing practice == bad practice.
anon
The motive for deriving from a generic class is specialization, not polymorphism.
Thomas L Holaday
+1  A: 

Maybe you would better use the pimpl idiom: it appears to me that you don't want to expose the implementation of your class to client code. If the vector and string objects are aggregated by value, the compiler needs to see their full declarations.

xtofl
+3  A: 

Just include the header in any file where you reference an STL collection.

As others have mentioned, there's not a way to reliably forward declare the STL classes, and even if you find one for your particular implementation, it will probably break if you use a different STL implementation.

If the compilation units don't instantiate the classes, it won't make your object files any bigger.

JohnMcG
+13  A: 

You cannot safely forward declare STL templates, at least if you want to do it portably and safely. The standard is clear about the minimum requirements for each of the STL element, but leaves room for implemtation extensions that might add extra template parameters as long as those have default values. That is: the standard states that std::vector is a template that takes at least 2 parameters (type and allocator) but can have any number of extra arguments in a standard compliant implementation.

What is the point of not including string and vector headers? Surely whoever is going to use your class must have already included it since it is on your interface.

When you ask about a reference to decide when to include and when to forward declare, my advice would be: include everything that is part of your interface, forward declare internal details.

There are more issues here that plain compilation performance. If you push the include of a type that is in your public (or protected) interface outside of the header you will be creating dependencies on the order of includes. Users must know that they must include string before including your header, so you are giving them one more thing to worry about.

What things should be included in the implementation file: implementation details, loggers, elements that don't affect the interface (the database connectors, file headers), internal implementation details (i.e. using STL algorithms for your implementation does not affect your interface, functors that are created for a simple purpose, utilities...)

David Rodríguez - dribeas
Old answer, I know, but I think the consensus of the standards committee was that it is *not* okay for an implementation to add extra parameters. They can do things like: `template <typename T, ...> class vector : private _VectorBase<T, ..., ExtraStuff> { ... };` to get the same effect.
GMan
+4  A: 

There is no simple obvious way to do it (as others have explained it very well).

However these headers should be seen as being part of the language (really!), so you can let them in your own headers without any problem, nobody will ever complain.

If your concern is compilation speed, I encourage you to use pre-compiled header instead and put these std headers in it (among other things). It will significantly increase your compilation speed.

Sorry the for the "real winner is the one who avoid the fight" kind of answer.

Jem
No need to say sorry, +1
peterchen
Thanks for the compilation speed tip.
bias
+1  A: 

With the exception of adding overloads to std::swap (the only exception I can think of right now), you are generally not allowed to add anything to the std namespace. Even if it were allowed, the actual declaration for std::vector is a lot more complicated than the code in the OP. See Nikolai N Fetissov's answer for an example.

All that aside, you have the additional problem of what your class users are going to do with functions that return a std::vector or std::string. The C++ Standard section 3.10 says that functions returning such objects are returning rvalues, and rvalues must be of a complete type. In English, if your users want to do anything with those functions, they'll have to #include <vector> or <string> anyway. I think it would be easier to #include the headers for them in your .h file and be done with it.

Kristo
It is possible to spare those users of his class who make use only of the methods that have neither vectors nor strings from including <vector> or <string>.
Thomas L Holaday
+1  A: 

I assume your objective here is to speed up compile times? Otherwise I'm not sure why you would want to remove them.

Another approach (not pretty but practical) is to use macro guards around the include itself.

e.g.

#ifndef STDLIB_STRING
#include <string>
#define STDLIB_STRING
#endif

Although this looks messy, on large codebases it does indeed increase the compile times. What we did is create a Visual Studio macro that will automatically generate the guards. We bind the macro to a key for easy coding. Then it just becomes a company coding standard (or habit).

We also do it for our own includes as well.

e.g.

#ifndef UTILITY_DATE_TIME_H
#include "Utility/DateTime.h"
#endif

Since we have Visual Studio helpers to auto-generate the guards when we create our own header files, we don't need the #define. The macro knows it's a internal include because we always use the

#include ""

format for our own includes and

#include <>

for external includes.

I know it doesn't look pretty but it did speed up our compile times on a largish codebase by over 1/2 hour (from memory).

Shane Powell