9

consider this translation unit:

#include <map>
#include <string>
int main()
{
   std::map<std::string, std::size_t> mp;
   mp.insert(std::make_pair("hello", 42)); 
}

There are two things in this translation unit that are bothering me, and they are

  • std::size_t
  • std::make_pair

I have just assumed that <cstddef> and <utility> must have been #included by <string> and <map>.
How rightful is this assumption? At least for make_pair I think there's a pretty strong guarantee because map member interfaces use std::pair. For std::size_t there is no formal guarantee but still it is very very very likely that it is available as soon as you include map or string. The stylistic question number one is Would you explicitly include <cstddef> and <utility> in this translation unit?

This part partly deals with the uncertaintly of some header being already included. However, there's the second part of the question. Suppose we have this

//f.h
#ifndef BIG_F_GUARD
#define BIG_F_GUARD
#include <string>
std::string f();
#endif   

//f.cpp
#include "f.h"
std::string f()
{
    std::string s;
    return s;
}

Second question is: Would you explicitly #include <string> into f.cpp?

I think I made my question clear. Btw. both questions are followed by a big WHY :) Thanks.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • this way its make a circular reference r8 ? – Sudantha Nov 25 '10 at 11:30
  • I believe that size_t is a standard type which should be included by the compiler because the operator `sizeof` returns an integer of type size_t. – Nick Nov 25 '10 at 11:38
  • @Nick: I think you have a microsoft compiler and are therefore mislead :) – Armen Tsirunyan Nov 25 '10 at 11:46
  • Errrr... yes I do have an MS compiler, and a GCC one and I'm fairly sure the standard implementations of `sizeof` return a `size_t`. But I might be wrong! :) – Nick Nov 25 '10 at 11:59
  • @Nick: I may be wrong too, but I don't remember that the standard mentions anywhere in its text that size_t is available in every translation unit regardless of the includes – Armen Tsirunyan Nov 25 '10 at 12:02
  • Yeah, you're right, the standard doesn't even mention size_t, however I guess I'm talking about 'standard implementations' which do appear to use `size_t` as the return type of the `sizeof` operator. Perhaps we're both right/wrong! – Nick Nov 25 '10 at 12:10
  • Hi Armen, good question - it raised a `3rd question' for me, if f() returns an integer should the string definition go in the header or cpp file? - seemed related enough to ask here - hope you don't mind – Tom Nov 25 '10 at 13:19
  • 2
    Hi @Tom: I don't mind at all :). I think (that's a personal preference) that a header should not include anything redundant, so if there are no string in the interface the .h should not include it, but if the definition uses string, the .cpp must include string – Armen Tsirunyan Nov 25 '10 at 13:23

6 Answers6

4

In the first case, I probably wouldn't but I should, if I want my code to be properly portable. There's no requirement that map::size_type is size_t, so there's no necessity for <map> to include a definition of size_t. For that matter, size_t can be a type alias rather than a distinct type, so even if size_type is size_t, it needn't have been defined in <map> using that name. So as you say, it's likely but not guaranteed that <map> includes <cstddef>.

In the second case, I definitely wouldn't, because I know I don't need to. IMO a .cpp file is entitled to rely on the headers included by its corresponding .h file, since you kind of expect that if you modify the .h file you potentially need to modify the .cpp file too -- change an interface implies change its implementation, most of the time. And even if you feel I'm not entitled, I can always document that f.h includes <string>, in which case I can rely on that.

Regarding <utility>, I don't know whether <map> is allowed to define std::pair (because it needs it) without defining std::make_pair (which is from the same standard header, and for the sake of argument let's say it isn't needed to define <map>). This would be possible if the implementation's version of <utility> itself includes a bunch of other files, for different bits, and <map> just includes the bit it needs. Specific permission is given for headers to include other headers, but I don't know whether specific permission is given for headers to put things in namespace std without including the whole of the corresponding header. The thing is, in practice it is very difficult to notice that you've forgotten a standard include in these cases, because implementations don't check for you, and that's why I know that in practice I'd quite likely not do it. Luckily it should be any easy fix for anyone porting to an implementation with different header dependencies.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
1

What I tend to do, and it's not necessarily the right thing to do, is to include all the files I need to get the module to compile. The only problem with this is that when dependencies change, you can end up with code included which isn't necessarily used. However a good compiler will normally deal with this.

There's no need to include <string> in your .cpp file however, because it's included through the header file. The contents of any included header files essentially get 'pasted' into your cpp file.

Hope this helps!

Nick
  • 25,026
  • 7
  • 51
  • 83
  • I see a contradiction between your first paragraph and the second. And this kind of contradiction has led me to ask the question :) – Armen Tsirunyan Nov 25 '10 at 11:34
  • I guess what my first paragraph is trying to say is that it's not going to do any harm in including it... especially if you know the code is getting used. But it's not necessary because you're already including it in the header file! – Nick Nov 25 '10 at 11:36
1
  1. No (as long as it's something you know it has to be in there on all platforms targeted; e.g. size_t due to it being one of the parameter/return types of string stuff)
  2. No (as the header file is your under your control and you know it's already included)
Mario
  • 35,726
  • 5
  • 62
  • 78
0

To answer your questions:

  1. No
  2. No

Include minimal stuff to make it compile, but no obsolete/additional dependencies that are already fulfilled

graham.reeds
  • 16,230
  • 17
  • 74
  • 137
  • I don't get it... you mean "Yes" "Yes"? – Armen Tsirunyan Nov 25 '10 at 11:33
  • Q1) Would you explicity include cstddef and utility. No Q2) Would you explicity include string in the cpp file. No. – graham.reeds Nov 25 '10 at 11:35
  • 1
    He's talking about "include minimal stuff to make it compile, but no obsolete/additional dependencies that are already fulfilled". – Mario Nov 25 '10 at 11:35
  • Exactly. I will take that and post it into my answer for more clarity. – graham.reeds Nov 25 '10 at 11:37
  • 4
    The fact that it compiles with your compiler doesn't mean that it will compile on any standards-compliant C++ compiler, because header files on one platform may be implemented differently on another platform. Some headers are guaranteed to be included by other headers by the standard and some are not. If you care about platform-independence, then you should rely on the former but not the latter. – Tim Martin Nov 25 '10 at 12:31
0

Atleast <map> is forced to include <utility> because the template for map looks like this:

namespace std {
template <class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T> > >
class map;
}

(Standard 23.3/1)

Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • Does that mean `` has to include ``, or just that it must have included some definitions? That is, is a standard header allowed to define `std::pair` but not `std::make_pair`? – Steve Jessop Jun 22 '11 at 11:56
0

My two bits:

  1. main.cpp: I would #include <cstddef> but probably not <utility> here. You are using std::size_t independent of whether it happens to be defined in the standard library headers. On the other hand, read the documentation on std::map and it is very clear that <map> must somehow define std::pair. There's no reason to #include <utility>. (This is particularly so since the connection between std::pair and is a bit of a WTF without reading the fine documentation.)

  2. f.hh: I would grudingly #include <string> here. Normally it is better to forward declare a class rather than #include the header that defines the class if the class is only used as a reference, a pointer, or a return type. The only reason to #include <string> in f.hh is because forward declaring std::string is a bit tough.

  3. f.cpp: I would #include <string> here. If the return type from f() was some class Foo rather than std::string, the expectation should be that the header merely forward declares Foo. The source file needs to #include the header that specifies class Foo. That the return type is std::string doesn't change this basic concept. Who knows? Some pedantically correct programmer might fix the problem in f.hh. (The problem with f.hh being that it #includes <string> when it doesn't really need to do so.)

David Hammen
  • 32,454
  • 9
  • 60
  • 108