Is it safe to assume that running g++
with
g++ -std=c++98 -std=c++11 ...
will compile using C++11? I haven't found an explicit confirmation in the documentation, but I see the -O
flags behave this way.
The GCC manual doesn't state that the
last of any mutually exclusive -std=...
options specified takes effect. The first occurrence
or the last occurrence are the only alternatives. There are numerous
GCC flags that take mutually exclusive alternative values from a finite set - mutually
exclusive, at least modulo the language of a translation unit. Let's call them mutex options for short.
It is a seemingly random rarity for it to be documented that the last setting takes effect. It is
documented for the -O
options as you've noted, and in general terms for mutually exclusive warning options, perhaps
others. It's never documented that the first of multiple setting takes effect, because
it's never true.
The documentation leans - with imperfect consistency - on the historical conventions of command usage in unix-likes OSes. If a command accepts a mutex option then the last occurrence of the option takes effect. If the command were - unusually - to act only on the first occurrence of the option then it would be a bug for the command to accept subsequent occurrences at all: it should give a usage error.
This is custom and practice. The custom facilitates scripting with tools that respect it, e.g. a script can invoke a tool passing a default setting of some mutex option but enable the user to override that setting via a parameter of the script, whose value can simply be appended to the default invocation.
In the absence of official GCC documentation to the effect you want, you might get reassurance by attempting to find any GCC mutex option for which it is not the case that the last occurrence takes effect. Here's one stab:
I'll compile and link this program:
main.cpp
#include <cstdio>
#if __cplusplus >= 201103L
static const char * str = "C++11";
#else
static const char * str = "Not C++11";
#endif
int main()
{
printf("%s\n%d\n",str,str); // Format `%d` for `str` mismatch
return 0;
}
with the commandline:
g++ -std=c++98 -std=c++11 -m32 -m64 -O0 -O1 -g3 -g0 \
-Wformat -Wno-format -o wrong -o right main.cpp
which requests contradictory option pairs:
-std=c++98 -std=c++11
: Conform to C++98. Conform to C++11.-m32 -m64
: Produce 32-bit code. Produce 64-bit code.-O0 -O1
: Do not optimise at all. Optimize to level 1.-g3 -g0
: Emit maximum debugging info. Emit no debugging info.-Wformat -Wno-format
. Sanity-check printf
arguments. Don't sanity check them.-o wrong -o right
. Output program wrong
. Output program right
It builds successfully with no diagnostics:
$ echo "[$(g++ -std=c++98 -std=c++11 -m32 -m64 -O0 -O1 -g3 -g0 \
-Wformat -Wno-format -o wrong -o right main.cpp 2>&1)]"
[]
It outputs no program wrong
:
$ ./wrong
bash: ./wrong: No such file or directory
It does output a program right
:
$ ./right
C++11
-1713064076
which tells us it was compiled to C++11
, not C++98
.
The bug exposed by the garbage -1713064076
was not diagnosed because
-Wno-format
, not -Wformat
, took effect.
It is a 64-bit, not 32-bit executable:
$ file right
right: ELF 64-bit LSB shared object, x86-64 ...
It was optimized -O1
, not -O0
, because:
$ "[$(nm -C right | grep str)]"
[]
shows that the local symbol str
is not in the symbol table.
And it contains no debugging information:
echo "[$(readelf --debug-dump right)]"
[]
as per -g0
, not -g3
.
Since GCC is open-source software, another way of resolving doubts
about its behaviour that is available to C programmers, at least,
is to inspect the relevant source code, available via git
source-control at
https://github.com/gcc-mirror/gcc.
The relevant source code for your question is in file gcc/gcc/c-family/c-opts.c
,
function,
/* Handle switch SCODE with argument ARG. VALUE is true, unless no-
form of an -f or -W option was given. Returns false if the switch was
invalid, true if valid. Use HANDLERS in recursive handle_option calls. */
bool
c_common_handle_option (size_t scode, const char *arg, int value,
int kind, location_t loc,
const struct cl_option_handlers *handlers);
It is essentially a simple switch ladder over option settings enumerated by scode
- which
is OPT_std_c__11
for option -std=c++11
- and leaves no doubt that it
puts an -std
option setting into effect regardless of what setting was in effect previously. You can look at branches other than master
(gcc-{5|6|7}-branch
) with the same conclusion.
It's not uncommon to find GCC build system scripts that rely on the validity of overriding an option setting by appending a new setting. Legalistically, this is usually counting on undocumented behaviour, but there's a better chance of Russia joining NATO than of GCC ceasing to take the last setting that it parses for a mutex option.