There are certain questions that tend to get asked over and over. This FAQ will attempt to answer the most common ones.
Q1: Why shouldnβt we use βusing namespace stdβ?
The statement using namespace std; is a using directive. A using-directive allows all of the identifiers from a given namespace to be accessed within the scope of the using-directive statement.
You may have seen something like this:
#include <iostream>
using namespace std;
int main()
{
cout << "Hello world!";
return 0;
}
This allows us to use names from the std namespace without having to explicitly type std:: over and over. In the above program, we can just type cout instead of std::cout. Sounds great, right?
However, when the compiler encounters using namespace std, it will make every single identifier in the std namespace available from the global scope (since thatβs where the using directive has been placed). This introduces 3 key challenges:
- The chance for a naming collision between a name youβve picked and something that already exists in the
stdnamespace is massively increased. - New versions of the standard library may break your currently working program. These future versions could introduce names that cause new naming collisions, or in the worst case, the behavior of your program might change silently and unexpectedly!
- The lack of
std::prefixes makes it harder for readers to understand what is astdlibrary name and what is a user-defined name.
For these reasons, we recommend avoiding using namespace std (or any other using-directive) entirely. The small savings in typing isnβt worth the additional headaches and future risks.
Related content
See lesson 7.13 -- Using declarations and using directives for more detail and examples.
Q2: Why can I use some function or type without including the header that declares that function or type?
For example, many readers ask why their program that uses std::string_view needs to #include <string_view> when it seems to work fine without the #include.
Headers can #include other headers. When you #include a header, you also get all of the additional headers that it includes (and all of the headers that those headers include too). All of the additional headers that come along for the ride that you didnβt explicitly include are called βtransitive includesβ.
In your main.cpp file, you probably #include <iostream>. On your compiler, if your <iostream> header includes the <string_view> header (for its own use), then when you #include <iostream> you will get the contents of the <string_view> header (and any other headers that <iostream> includes). This means your main.cpp will be able to use the std::string_view type without explicitly including <string_view> header.
Even though this may compile on your compiler, you should not rely on this. What compiles for you now may not compile on another compiler, or even on a future version of your compiler.
There is no way to warn when this happens, or prevent it from happening. The best you can do is take care to explicitly include the proper headers for all of the things you use. Compiling your program on several different compilers may help identify headers that are being transitively included on other compilers.
Related content
Covered in lesson 2.11 -- Header files.
Q3: My code that produces undefined behavior appears to be working fine. Is this okay?
aka: βI did that thing you told me not to do, and it worked. So whatβs the issue?β
Undefined behavior occurs when you perform an operation whose behavior is not defined by the C++ language. Code implementing undefined behavior may exhibit any of the following symptoms:
- Your program produces a different result every time it is run.
- Your program behaves inconsistently (sometimes produces the desired result, sometimes not).
- Your program consistently produces the same incorrect result.
- Your program initially seems like its working but produces some incorrect result later in the program.
- Your program crashes, either immediately or later.
- Your program works on some compilers or platforms but not others.
- Your program works until you change some other seemingly unrelated code.
- Your program appears to produce the desired result anyway.
One of the biggest problems with undefined behavior is that the behavior exhibited by the program may change at any point, for any reason. So while such code may appear to work now, there is no guarantee it will do so when run again later.
Related content
Undefined behavior is covered in lesson 1.6 -- Uninitialized variables and undefined behavior.
Q4: Why does my code that produces undefined behavior generate a certain result?
Readers often ask what is happening to produce a specific result on their system. In most cases, itβs difficult to say, as the result produced may be dependent upon the current program state, your compiler settings, how the compiler implements a feature, the computerβs architecture, and/or the operating system. For example, if you print the value of an uninitialized variable, you might get garbage, or you might always get a particular value. It depends on what type of variable it is, how the compiler lays out the variable in memory, and whatβs in that memory beforehand (which might be impacted by the OS or the state of the program prior to that point).
And while such an answer may be interesting mechanically, itβs rarely useful overall (and likely to change if and when anything else changes). Itβs like asking, βWhen I put my seat belt through the steering wheel and connect it to the accelerator, why does the car pull left when I turn my head on a rainy day?β The best answer isnβt a physical explanation of whatβs occurring, itβs βdonβt do thatβ.
Q5: Why am I getting a compile error when I try to compile an example that seems like it should work?
The most common reason for this is that your project is being compiled using the wrong language standard.
C++ introduces many new features with each new language standard. If one of our examples uses a feature that was introduced in C++17, but your program is compiling using the C++14 language standard, then it probably wonβt compile because the feature weβre using isnβt supported by the language standard weβve selected.
Try setting your language standard to the latest version your compiler supports and see if that resolves the issue. You can also check that your compiler is properly configured to use the language standard youβre expecting by running the program here: 0.13 -- What language standard is my compiler using?.
Related content
Covered in lesson 0.12 -- Configuring your compiler: Choosing a language standard.
It is also possible that your compiler either doesnβt support a specific feature yet, or has a bug preventing use in some cases. In this case, try updating your compiler to the latest version available.
The CPPReference website tracks compiler support for each feature per language standard. You can find those support tables linked from their home page, top right, under βCompiler Supportβ (by language standard). For example, you can see which C++23 features are supported here.
Q6: Why should I #include βfoo.hβ from foo.cpp?
It is a best practice for a source file (e.g. foo.cpp) to include its paired header (e.g. foo.h). In many cases, foo.h will contain definitions that foo.cpp will need to compile correctly.
However, even if foo.cpp compiles file without foo.h, including the paired header allows the compiler to discover certain types of inconsistencies between the two files (e.g. when the return type of a function doesnβt match the return type of its forward declaration). Without the inclusion, this might cause undefined behavior.
The cost of the #include is negligible, so there is little downside to including the header.
Related content
Covered in lesson 2.11 -- Header files.
Q7: Why does my project only compile when I #include βfoo.cppβ from βmain.cppβ?
This is almost always due to forgetting to add foo.cpp to your project and/or compilation command line. Update your project and/or command line to include each source (.cpp) file. When you compile your project, you should see each source file being compiled.
Normally in a project that has multiple files, the compiler will compile each source (.cpp) file individually. After all the source files have been compiled, the linker links them together and creates the resulting output file (e.g. an executable). However, if you split your code between two or more files (e.g. main.cpp and foo.cpp) and then only compiles main.cpp, you will probably get a compilation error or linker error, since part of the code required for your project is not being compiled.
New programmers sometimes discover that they can make their program work by adding #include "foo.cpp" to main.cpp instead of adding foo.cpp to the project or compilation command line. After doing so, when main.cpp is compiled, the will preprocessor will create a translation unit consisting of the code from both foo.cpp and main.cpp, which will then be compiled and linked. In smaller project, this may work. So why not do this?
There are a few reasons:
- It can result in naming collisions between files.
- It can be hard to avoid ODR violations.
- Any change to any .cpp file will result in your entire project being recompiled. This can take a long time.
Related content
Covered in lesson 2.11 -- Header files.
Q8: Why do I need to return 0 at the bottom of main?
You donβt. The main() function is special in that it will implicitly return 0 if you do not provide a return statement.
However, any other value-returning function that reaches the end of its body without encountering a return statement will produce undefined behavior.
For consistency, we recommend explicitly returning 0 from main(). But if you prefer to omit the return statement in main() for conciseness, you can. Just donβt make the mistake of thinking other value-returning functions work similarly.
Related content
Covered in lesson 2.2 -- Function return values (value-returning functions).
Q9: When I compile an example from this website, I get an error similar to βargument list for class template XXX is missingβ. Why?
The most likely reason is that the example makes use of a feature called Class Template Argument Deduction (CTAD), which is a C++17 feature. Many compilers default to C++14, which doesnβt support this feature.
If the following program doesnβt compile for you, thatβs the reason:
#include <utility> // for std::pair
int main()
{
std::pair p2{ 1, 2 }; // CTAD used to deduce std::pair<int, int> from the initializers (C++17)
return 0;
}
Related content
You can check which language standard your compiler is configured for using the program in lesson 0.13 -- What language standard is my compiler using?.
We cover CTAD in lesson 13.14 -- Class template argument deduction (CTAD) and deduction guides.
Q10: Why donβt we make function parameters or return types const when passing or returning by value?
We typically donβt const by-value function parameters because:
- Making such parameters
constoffers no meaningful value to the caller of the function, but adds clutter to the functionβs interface. - We generally donβt care if the function modifies those parameters, since they are copies that will be destroyed at the end of the function anyway.
We donβt const by-value return values because:
- If the return type is a non-class-type (e.g. a fundamental type), const will be ignored anyway.
- If the return type is a class type, const may inhibits certain kinds of optimizations (e.g. move semantics).
Note: const is relevant when passing/returning by address or by reference.
Related content
Covered in lesson 5.1 -- Constant variables (named constants)
Q11: Why should I use constexpr?
Constexpr and other compile-time programming techniques provide many benefits, including:
- Smaller and faster code.
- We can have the compiler detect certain kinds of errors and halt compilation if they occur.
- Undefined behavior is not allowed at compile-time.
- The ability to use variables and functions in places that require a constant expression.
The last point is perhaps the most unavoidable, as certain C++ features require values that are known at compile-time.
Related content
Covered in lesson 5.5 -- Constant expressions
Q12: Why should I constexpr an eligible function when I know it will only be evaluated at runtime in my program?
There are a few reasons you might want to do so:
- Thereβs little downside to using constexpr, and it may help the compiler optimize even in contexts where it isnβt evaluated at compile-time.
- Just because youβre not calling the function in a compile-time evaluatable context right now doesnβt mean you wonβt call it in such a context when you modify or extend your program. And if you havenβt constexprβd the function already, you may not think to when you do start to call it in such a context, and then youβll miss out on the performance benefits. Or you may be forced to constexpr it later when you need to use the return value in a context that requires a constant expression somewhere.
- Repetition helps ingrain best practices.
On a non-trivial project, itβs a good idea to implement your functions with the mindset that they may be reused (or extended) in the future. Any time you modify an existing function, you risk breaking it, and that means it needs to be retested, which takes time and energy. Itβs often worth spending an extra minute or two βdoing it right the first timeβ so you donβt have to redo (and retest) it again later.
Related content
Covered in lesson F.1 -- Constexpr functions
Q13: Why shouldnβt I call the same input function more than once in an expression?
In most cases, the C++ standard does not specify the order in which operands (including function arguments) are evaluated. The precedence and associativity of operators is used only to determine how operands are grouped with operators, and the order of value computation.
For example, given the statement std::cout << subtract(getUserInput(), getUserInput()), either the left argument or the right argument in the function call to subtract() could be evaluated first. Letβs say the user inputs the values 5 and 2. If the left argument was evaluated first, the left argument will evaluate to 5, and the right argument will evaluate to 2. 5 - 2 is 3. If the right argument was evaluated first, the right argument will evaluate to 5, and the left argument will evaluate to 2. 2 - 5 is -3. Thus, this statement might print either 3 or -3.
This can be disambiguated by making each function call to getUserInput() a separate statement (so the order is deterministic), and storing the return value in a variable until it is needed.
Related content
Covered in lesson 6.1 -- Operator precedence and associativity
Q14: There arenβt enough quizzes! Where can I get more practice?
We recommend https://www.codewars.com/, which has plenty of bite-sized exercises to help improve your general problem-solving and C++ implementation skills. Itβs also fun!
Once you have a solution, you can compare it with the answers of others to see alternative approaches, or understand where your own code might be improved.
However, because disposable exercises have disposable answers, solving such quizzes doesnβt really encourage writing high quality code, or demonstrate what happens when you donβt follow best practices. For that, nothing is better than creating your own project.
Start with something small -- a simple game or simulation. Then layer in features over time. As the complexity of your project increases, youβll start the see the various deficiencies in your code show up. This will help you identify what areas of your code are in need of quality improvements.
