Compiler behaviour quiz

Started by
7 comments, last by SiCrane 11 years, 2 months ago

Here's some example code:


#include <iostream>
 
using namespace std;
 
struct number { float x; };
 
number test_func(void) { static number v; return v; }
 
int main()
{
    const number one = { 1.0f };

    test_func() = one;
    test_func().x += 1.0f;
    
    cout << test_func().x << endl;
    
    return 0;
}

Mini quiz:

1. Without using a compiler look at the code. Would you expect any warnings or errors from the compiler? Why?

2. What would you expect the output to be if it compiled?

3. Now compare your results to what actually happens when you compile it. Is it what you expected?

Advertisement
your static is returned as a copy...

[edit]A nice compiler might warn you that your assignments are to a temporary, and won't have any real effect in this case.

your static is returned as a copy...

I know. That's why the results are interesting. Try it on a few compilers ;)

If I haven't missed anything it should print "0" on all compilers.

Edit: And report no warnings or errors, except maybe about unused stuff.

[spoiler]

As you've hopefully found out by now on some compilers the code doesn't produce a single error or warning. This is the result from Clang:

test.cpp:14:19: error: expression is not assignable
test_func().x += 1.0f;
~~~~~~~~~~~~~ ^

1 error generated.

I was expecting at least a warning from most compilers - the code is clearly not sensible. Clang didn't even pick up the other line of code that is equally bad...
[/spoiler]

Good to know that there's yet another instance where Clang points out non sensical code. I'm not sure it's correct though, but too lazy to check it... :)

Clang didn't even pick up the other line of code that is equally bad...

From the compiler's point of view it's not equally bad. For test_func().x += 1.0f, the compiler will see test_func() is an rvalue so test_func().x is also an rvalue, so if a primitive rvalue appears on the left side of an = then you've got a problem. However, for test_func() = one, it doesn't think of the = as a real =, it sees that the left hand side is a class type, so this is actually test_func().operator=(one). The . operator when the right hand side is a member function doesn't care if the left hand side is an rvalue, it only looks for the cv-qualifier such as const or non-const. In this case test_func() returns a non-const object so it can be bound to the compiler generated number & number::operator=(const number &) function.
Apparently GCC catches it also. Here's my expected results before compiling and before reading others' comments:

1. Without using a compiler look at the code. Would you expect any warnings or errors from the compiler? Why?

"test_func() = one;"- Some kind of 'Assigning to r-value/temporary' warning, since test_func() isn't returning by reference.

Also, test_func(void) should be test_func(), no need for the 'void' there. I don't know if a warning will result. Probably something like "explicit void argument - compile with -ANSI to disable warning"

2. What would you expect the output to be if it compiled?

Undefined gibberish value - whatever 'static number v' initially resolves to, because it has never been assigned anything.

3. Now compare your results to what actually happens when you compile it. Is it what you expected?

Using GCC/MinGW 4.7.2:

I didn't get the warning I expected on assigning "test_func() = one;",
instead I got an *error* on on the member variable assignment version: "test_func().x += 1.0f;"

error: using temporary as lvalue [-fpermissive]

[hr]

I only found out yesterday that this is valid and standardized C++:
const int &refToTemp = 357;
...it just only lasts to the end of the scope that the expression is contained in. Still it's useful for avoiding a copy in some circumstances, which is why I looked it up:
//Only makes a copy if 'condition' is false.
const std::string &myString = ( ...condition... ? this->memberString : this->GetCopyOfSubStr_ReturnByValue());
this->UseString(myString);

Also, test_func(void) should be test_func(), no need for the 'void' there.

(void) is a perfectly standard, legal alternative to () for function declarations that aren't destructors. It's not necessary, but neither is white space in most cases. Since (void) and () have a different meaning in C, giving a warning for this would annoy quite a few people who write or use headers that are meant to be used from both C and C++. And since that includes the C standard library headers, which every C++ compiler that is also a C compiler will have, this isn't a warning you'll find very often.

Undefined gibberish value - whatever 'static number v' initially resolves to, because it has never been assigned anything.

Objects with static storage duration, which includes function local static variables, are automatically zero-initialized before any other initialization takes place (if any). Since the class type lacks any implicit or explicit non-trivial constructors and the object has no other initialization, then zero initialization is the only thing that happens in this case.

This topic is closed to new replies.

Advertisement