Here be dragons

    Looking through the materials of the GoingNative 2012 conference (which I strongly advise all C ++ programmers to look at), I drew attention to one example code:

    #include 
    struct  S { int  n; };
    struct  X { X(int) {} };
    void f(void*) {
        std::cerr << "Pointer!\n";
    }
    void f(X) {
        std::cerr << "X! \n";
    }
    int  main() {
        f(S().n);
    }
    

    Can you, without peeking back, say what this program will print and most importantly why ?

    Under the cut, Google’s Clang developer ’s suggestion is why this code works the way it works. Once again, who didn’t catch it: the developer of the C ++ compiler from Google does not know this for sure, he just has an assumption.

    Answer


    When compiled in accordance with the C ++ 98 standard, this code will print “ X! ”, When compiled in accordance with the C ++ 98 standard, this code will print “ Pointer! ”.

    # clang++ -std=c++98 -g -o cxx11-4 cxx11-4.cpp
    # ./cxx11-4
    X!
    # clang++ -std=c++11 -g -o cxx11-4 cxx11-4.cpp
    # ./cxx11-4
    Pointer!
    


    Question to the developers of the C ++ 11 standard




    Explanations


    Let's look carefully at the line

    f(S().n);
    

    As you can see, an instance of the S structure is created here. It does not have an explicit constructor, which means that the default constructor is called. And here enters the scene Standard C ++ 11 with its advanced support of constant expressions ( Generalized constant expressions ). Any function (including a constructor) in C ++ 11 can be declared as always returning the same expression. This is done to be able to write this code:

    int get_five() {return 5;}
    int some_value[get_five() + 7]; 
    

    Constant expressions are used in array declarations, in enums, in switch \ case blocks. And the C ++ 11 compiler tries any function that can be constant, considered constant, in order to be able to use it in all these places. But what about the constructor of structure S ? Well, if he always assigns a certain number to the variable n (and the standard does not prohibit this), then it can also be a constant expression. And why would he assign n different values ​​each time? Assigns zero. Why exactly zero? Do you have any smarter meaning on your mind?
    So, the above line is equivalent:

    f(0);
    

    Well, this, as we know, is completely equivalent:

    f(NULL);
    

    And this translates to void * rather than to struct X (even with the corresponding constructor X (int) ). And here we have the explicit call void f (void *) ! Well, it prints " Pointer! ".

    Why doesn't this happen in a compiler with C ++ 98 support? Yes, because he does not have this very advanced support for constant expressions. The default constructor of structure S has no reason to set property n to zero (the standard does not require this). Well, he doesn’t. So the string f (S (). N); cannot be unambiguously converted to f (0); with all the ensuing consequences.

    Conclusion


    The C ++ 11 standard is new; its support in compilers is also raw. Be prepared for such surprises.

    Also popular now: