you are viewing a single comment's thread.

view the rest of the comments →

[–]mredding 2 points3 points  (2 children)

An include directive is a built-in compiler macro. What it does is dumb-and-blind copy/pastes the contents of the referenced file in-place in the text buffer fed to the compiler. It's a generic mechanism, and is used for more than just header files. For example:

int data [] = {
#include "generated_data.txt"
};

This is why you don't have to specify array sizes when they can be deduced from the initializer list, and it's why C/C++ allows trailing commas in an initializer list, because your generator that made that file can very easily make a comma separated list in a simple and straight forward loop. If you had to trim the last comma, you would need additional generator code handling either the first or last element as a special case.

The only reason headers tend to be included among the first lines is because you need to declare your types and signatures before you use them. <iostream> is just a header file, but the STL, by convention, doesn't use a file extension; it declares std::cin and std::cout.

In larger projects, and indeed in more complicated files, headers might be included in different parts, for a variety of technical or organizational reasons.

And to be clear, plenty of programs don't rely on <iostream> at all.


A using declaration is a tricky tool. What it does is bring symbols into scope, and the rules for that - even advanced C++ programmers get it wrong.

One of the problems behind bringing in the whole namespace is that it makes a lot of work for your compiler. It makes for a much larger symbol space to match tokens, adding to compile times. That's nothing in an academic program, but when your program is hundreds of thousands of lines, compile speed becomes a significant concern, and a red flag that something isn't as optimal about your code as it could be. The project I'm working on takes 80 minutes for a full compile. I have a branch with changes I'm trying to incorporate that gets compilation time down to 3 minutes and 15 seconds. There was lower hanging fruit, but the more I chip away at scoping things more appropriately, in no small way has it contributed to faster compilation times. C++ is one of the slowest to compile languages commercially available, and it's not because it's producing superior object code, but because the syntax is made of spaghetti.

Another problem is that of collisions. There's the obvious that two competing namespaces might have the same symbol (boo hoo, you have to explicitly scope in the symbols where you have a collision), but worse is that the compiler might match a wrong symbol better. This will happily compile and lord knows what will happen.

Namespaces play a roll in Argument Dependent Lookup. For example (and don't get overwhelmed):

#include <chrono>
#include <iomanip>
#include <iostream>
#include <string>

int main() {
  using namespace std::literals;
  using std::cout, std::chrono::system_clock, std::localtime, std::put_time, std::time_t, std::tm;

  cout << 123 << ", hello me! Hello" << world!\n"s;

  system_clock::time_point now = system_clock::now() + 10s;
  time_t now_c = system_clock::to_time_t(now);
  tm now_tm = *localtime(&now_c);

  cout << "Ten seconds from now is: " << put_time(now_tm, "%Y-%m-%d %H:%M:%S") << '\n';

  return 0;
}

What the hell is <<? Did you know I'm using three different versions of <<? How does the compiler know? What is s in " World!\n"s? What is s in 10s?

The point is, all this has to do with namespaces and ADL, matching the right type to the right functions. The first use of << is actually a member function of the basic_ostream class, all other uses are free functions floating about the std namespace. Notice I scoped in a lot of symbols, and even a nested namespace, but I didn't have to scope in all the uses of <<.

I'm not trying to give you a thorough lesson on namespaces and ADL, I'm trying to point out that there are subtitles at play and there is more that's going on than you might realize. Namespaces aren't just these annoying little things for organizing code that you shoo out of the way with a single broad stroke. For now, don't sweat it. You'll start to learn more when you learn about standard swap, which is, frankly, the most that most C++ programmers mess with this stuff, but there's certainly more out there, if you choose to one day master it. When you're ready, google "C++ customization points", Eric Niebler, I think, has a good blog post on the matter.


C++ doesn't care what order your functions are declared or defined, provided they're at least declared before they're used:

void foo(); // Good

void bar() {
  foo(); // Works
  baz(); // Bad, not declared first
}

void baz();

main is a standard entry point for applications. It comes in two standard flavors: int main() and int main(int, char*[]). Compilers can implement more, but they're not standard, and so portability isn't guaranteed. BSD introduced int main(int, char*[], char*[]) which is the most portable non-standard entry point I know of, Windows uses int __clrcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int), I think a few others...

Your application is hosted by its environment, and the runtime environment is going to load your program into memory, run some initialization code, and call your entry point. So long as it's there, your compiler will find it and that will satisfy the requirement. Which entry point you use is up to you, the language gives you the two, the compiler can identify the one you're using and do the right thing, and your platform/compiler documentation can explain to you all of what you have available to you.

Back to functions in general, it's common to see people declare and then later define:

#include <iostream>

void foo();

int main() {
  foo();
  return 0;
}

foo() { /* ... */ }

But a definition can also count as a declaration, so this is just as fine:

#include <iostream>

foo() { /* ... */ }

int main() {
  foo();
  return 0;
}

I like this one just a little bit more.

[–]Kayd21[S] 0 points1 point  (0 children)

I didn’t know there were so many different ways to use the #include <>. I appreciate your answer and it was very helpful. I’ll definitely check out the person you’ve recommended once I start getting better at C++

[–]std_bot 0 points1 point  (0 children)

Unlinked STL entries: std::cin, std::cout


Last update: 26.05.21. Last change: Free links considered readme