Aristotle0
Aristotle0

Reputation: 317

Why is the header file compiled successfully without include statement?

The codes below is compiled successfully, but I think it lacks #include<string>, why? If I just include string not vector, it fails. Could anybody give an explanation?

StrBlob.hpp:

#ifndef STRBLOB_H_ 
#define STRBLOB_H_ 
#include <vector> 
class StrBlob { 
 public: 
    typedef std::vector<std::string>::size_type size_type; 
    StrBlob(); 
    StrBlob(std::initializer_list<std::string> i1); 
    bool empty() const {return data->empty();} 
    void push_back(const std::string &s) {data->push_back(s);} 
    void pop_back(); 
    std::string &front(); 
    std::string &back(); 
 private: 
    std::shared_ptr<std::vector<std::string>> data; 
    void check(size_type i, const std::string &msg) const; 
}; 
#endif

compile:

g++ StrBlob.hpp

I only compile this file without others. It compiles successfully.

update

If I add a main file:

main.cc

#include "StrBlob.hpp"
int main() {
    int i = 0;
    return 0;
}

just do:

g++ main.cc

It fails. The compilation output:

In file included from test_strblob.cc:1:
In file included from ./StrBlob.hpp:3:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/vector:1540:9: error: implicit
      instantiation of undefined template 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >'
        ++this->__end_;
        ^
./StrBlob.hpp:11:49: note: in instantiation of member function 'std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >
      >::push_back' requested here
    void push_back(const std::string &s) {data->push_back(s);}
                                                ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/iosfwd:188:33: note: template is declared
      here
    class _LIBCPP_TYPE_VIS_ONLY basic_string;
                                ^
In file included from test_strblob.cc:1:
In file included from ./StrBlob.hpp:3:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/vector:265:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__bit_reference:15:

▽
  3 #ifndef STRBLOB_H_
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/algorithm:627:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/memory:1641:31: error: implicit

▽
      instantiation of undefined template 'std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >'
            ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
                              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/memory:1568:18: note: in instantiation of
      function template specialization 'std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>
      > >::construct<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, const std::__1::basic_string<char,
      std::__1::char_traits<char>, std::__1::allocator<char> > &>' requested here
            {__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
                 ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/memory:1449:14: note: in instantiation of
      function template specialization 'std::__1::allocator_traits<std::__1::allocator<std::__1::basic_string<char,
      std::__1::char_traits<char>, std::__1::allocator<char> > > >::__construct<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &>' requested
      here
            {__construct(__has_construct<allocator_type, pointer, _Args...>(),
             ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/vector:1538:25: note: in instantiation of
      function template specialization 'std::__1::allocator_traits<std::__1::allocator<std::__1::basic_string<char,
      std::__1::char_traits<char>, std::__1::allocator<char> > > >::construct<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::allocator<char> >, const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &>' requested
      here
        __alloc_traits::construct(this->__alloc(),
                        ^
./StrBlob.hpp:11:49: note: in instantiation of member function 'std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >
      >::push_back' requested here
    void push_back(const std::string &s) {data->push_back(s);}
                                                ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/iosfwd:188:33: note: template is declared
      here
    class _LIBCPP_TYPE_VIS_ONLY basic_string;
                                ^
2 errors generated.

It needs #include <string>. I think the reason may be as Peter Clark said.

Upvotes: 2

Views: 1506

Answers (3)

Peter Clark
Peter Clark

Reputation: 2943

On some platforms some system headers will include files that other platforms don't. On any version of MSVC I've used <vector> includes <string>. On GCC - to my experience - it doesn't so this code would not compile. Generally you want to make sure to include every thing you use, even if it compiles without.

To see if this is the case with your compiler you can save preprocesser output to a file (for example, this is how you do it in GCC and MSVC). Then you can search for basic_string and see where it was included from. In my case with MSVC:

#line 7 "w:\\program files (x86)\\microsoft visual studio 12.0\\vc\\include\\vector"
#line 1 "w:\\program files (x86)\\microsoft visual studio 12.0\\vc\\include\\stdexcept"

#line 1 "w:\\program files (x86)\\microsoft visual studio 12.0\\vc\\include\\xstring" 

You can see that vector includes stdexcept which includes xstring which is the where the implementation of std::basic_string is defined for MSVC

Note that you could also just look at the standard headers , but it might be hidden somewhere that isn't obvious (I didn't expect it to be through <stdexcept>).

The moral of the story is that even if it compiles, be sure you are always including what you use in order to avoid running into issues from this later down the road (this goes for including files in general, not just the standart library).

Response to Update

As others have said, all #include does is essentialy paste the code from the header file where the include directive is. So after preprocessing (when includes are "pasted"), what the compiler will see is:

// contents of <vector> header would be pasted here

class StrBlob { 
 public: 
    typedef std::vector<std::string>::size_type size_type; 
    StrBlob(); 
    StrBlob(std::initializer_list<std::string> i1); 
    bool empty() const {return data->empty();} 
    void push_back(const std::string &s) {data->push_back(s);} 
    void pop_back(); 
    std::string &front(); 
    std::string &back(); 
 private: 
    std::shared_ptr<std::vector<std::string>> data; 
    void check(size_type i, const std::string &msg) const; 
};

int main() {
    int i = 0;
    return 0;
}

The primary difference between before and now is that now you are linking as well as compiling. At this point though I'm honestly not sure exactly what it is causing this, but that isn't something that's worth knowing in my opinion since it is just an implementation detail of your compiler's standard library.

The main thing to know is to always include what you use (you're also missing <memory> for std::shared_ptr). What system headers may or may not include is implementation dependent and thus not something you should ever rely on or (hopefully) need to worry about.

Upvotes: 4

bereal
bereal

Reputation: 34252

This may compile because the including code looks like this:

#include <string>
// ...
#include "strblob.h"
// ...

Including a header is a purely textual operation done by the preprocessor before the compiler steps in, so the code inside strblob.h will see the content of <string>. One should not rely on those "lucky" indirect dependencies though.

Upvotes: 0

FatalError
FatalError

Reputation: 54541

The C++ preprocessor basically takes all your #include directives and basically pastes them into one long stream for the compiler.

You may not have included it here, but it was probably included somewhere else earlier. For correctness, you should include it also, however. Creating implicit dependencies can make your code hard to maintain and fragile (when it comes to build errors), so avoid them wherever you see them.

Upvotes: 4

Related Questions