C++ / GETTING STARTED
Install a compiler and verify it with g++
Install a working C++ toolchain and verify it end to end: which g++ answers, which -std is active, and whether libstdc++ headers match.
What you will learn
- Confirm which compiler answers with g++ --version and which g++ before trusting it.
- Build and run with g++ -std=c++17 -Wall file.cpp -o prog && ./prog
- Read __cplusplus to prove which -std flag actually reached the compiler.
- Explain why gcc fails to link C++ programs and why macOS g++ is really clang.
Understanding Install a compiler and verify it with g++
Installing a C++ compiler installs three things that merely happen to ship together: the driver program named g++, the C++ frontend that driver launches (cc1plus in GCC's case), and the standard library, meaning both the headers such as <string> and the runtime object libstdc++ that your executable links against. When you type g++, the shell walks PATH and runs the first match; that binary then finds its own sibling pieces through paths baked in when it was built. So "is the compiler installed" is really three questions, and a broken setup almost always means one piece is missing or a different g++ answered than the one you thought.
Package names differ per platform: build-essential on Debian and Ubuntu, gcc-c++ on Fedora, MSYS2 or WSL on Windows. On macOS, /usr/bin/g++ is a compatibility name for Apple clang, so g++ --version prints "Apple clang version" and no GCC was ever involved. Because that substitution is silent, the only verification worth doing is behavioural: compile a file you wrote yourself and run the binary it produced. An installer that reported success tells you nothing about which compiler your shell will actually reach.
Compiler version and language standard are independent knobs. A g++ 13 install will compile in C++11 mode without complaint; the -std flag picks the language level, and the compiler records that choice in the macro __cplusplus, which is the only dependable way to see what took effect. Library support is a third axis: the flag can be accepted while the installed headers are older than the feature you want, which is why a check that calls a C++17 library function proves more than one that uses only core syntax.
// build: g++ -std=c++17 -Wall -Wextra verify.cpp -o verify
<iostream>
<string>
int main() {
std::string probe = "toolchain";
std::cout << "headers found, string works: " << probe.size() << " chars\n";
std::cout << "__cplusplus reports: " << __cplusplus << '\n';
std::cout << "at least C++17: " << (__cplusplus >= 201703L ? "yes" : "no") << '\n';
std::cout << "frontend: clang\n";
std::cout << "frontend: gcc\n";
std::cout << "frontend: something else\n";
}
g++ is a driver on your PATH that ties together a C++ frontend, a set of standard headers, and libstdc++, so verifying an install means proving all three cooperate to produce a binary that runs.
Worked examples
Which standard is actually active
Shows that the -std flag, not the compiler's version number, decides the language level the build uses.
// build: g++ -std=c++11 probe.cpp -o probe
<iostream>
int main() {
std::cout << "__cplusplus = " << __cplusplus << '\n';
std::cout << "mode: C++23 or newer\n";
std::cout << "mode: C++20\n";
std::cout << "mode: C++17\n";
std::cout << "mode: C++14\n";
std::cout << "mode: C++11\n";
std::cout << "mode: C++98 or C++03\n";
}
Example explained
Line 1__cplusplus is set by the compiler from the -std flag, so a brand new g++ prints 201103 here.
Line 2The comparisons use >= because the standard values only ever increase: 201103 < 201402 < 201703 < 202002.
Line 3The #elif chain is resolved before compilation, so exactly one output line exists in the finished binary.
Line 4Rebuilding the identical file with -std=c++17 changes the number to 201703 without editing a character.
Headers must be new enough too
Checks the installed standard library, not just the driver, by calling a function that only exists from C++17 onward.
// build: g++ -std=c++17 libcheck.cpp -o libcheck
<iostream>
<numeric> // std::gcd arrived with C++17
int main() {
std::cout << std::gcd(84, 36) << '\n';
std::cout << "libstdc++ supplies C++17 numerics\n";
}
Example explained
Line 1std::gcd lives in <numeric> and was added by C++17; passing -std=c++17 does not conjure it into an old header.
Line 2On a libstdc++ older than GCC 7 this fails with "gcd is not a member of std" even though the flag was accepted.
Line 3A clean build therefore proves the header set that shipped alongside the driver is as new as the flag claims.
Line 484 and 36 share the factors 2, 2 and 3, so the printed greatest common divisor is 12.
Important notes
__GNUC__ is defined by clang and several other compilers for compatibility, so check __clang__ before __GNUC__ whenever you need to know which frontend is really running.
-std=c++17 and -std=gnu++17 both set __cplusplus to 201703L, but the gnu variant additionally enables GNU extensions, which is how code that builds under a bare g++ invocation can fail on a stricter compiler.
Common mistakes
Building with gcc instead of g++: the .cpp extension still selects the C++ frontend, so compilation succeeds, but libstdc++ is left off the link line and you get "undefined reference to std::cout" and conclude your code is wrong.
Assuming g++ on macOS is GCC: /usr/bin/g++ dispatches to Apple clang, so version strings, warning wording and GCC-only flags will not match what you read, and because clang also defines __GNUC__ the swap stays invisible unless you test __clang__ first.
Editing verify.cpp and rerunning ./verify without recompiling: the executable on disk is still the old build, the output never changes, and you wrongly decide the -std flag or the install had no effect.
Try it yourself
Change, predict, then run
In a browser editor, run a program that prints __cplusplus together with an #if chain naming the standard, then switch the editor's language-standard setting from C++11 to C++20 and run it again. Record both printed numbers and confirm you never changed the source.
Open the C++ workspaceCheck your understanding
The same file builds and runs with g++ prog.cpp -o prog, but with gcc prog.cpp -o prog it fails at the link step with "undefined reference to std::cout". What does that tell you?
- The gcc on this machine is an older release than the g++ on this machine.
- gcc ignores the .cpp extension and compiles the file as C, so std::cout is never declared.
- gcc compiles the file as C++ but leaves libstdc++ off the link line, so the library symbols have no definition.
- The linker needs -std=c++17 in order to locate the standard library.
Show answer
Both drivers pick the language from the file extension, so the C++ code compiled fine and the problem appeared only when the linker looked for definitions; g++ is the same driver configured to add the C++ standard library. Option 1 is tempting because "gcc" sounds like a C-only tool, but if the file had been compiled as C you would have seen compile-time errors about unknown names, not a link-time undefined reference. The -std flag affects the language level during compilation, not what the linker pulls in.