gccg++compiler-options

Passing multiple -std switches to g++


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.


Solution

  • 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:

    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.