Home > database >  Why is the return value incorrect with optimisation flags on?
Why is the return value incorrect with optimisation flags on?

Time:06-24

I am (for myself only) practicing c and trying to "re-code" c# properties into it. I do know it is useless (I mean, I will not use it) however, I just wanted to try to see if this was possible.

For some reasons, with the following code, under the latest clang / gcc version, it does not yeld the correct result under every single optimisation flags other than -O0 (which disables optimisation as far as I am aware of.)

#include <functional>
#include <iostream>

template<typename T>
class Property
{
private:
    using getType = std::function<const T&(void)>;
    using setType = std::function<void(const T&)>;

    T internal;

    getType get;
    setType set;
public:
    Property(const T &value) : internal{value}, get{[this] () { return internal; }}, set{[this](const T& value) { internal = value; }} {}
    Property(getType fnc) : get(fnc) {  }
    Property(getType fncGet, setType fncSet) : get(fncGet), set(fncSet) {  }

    Property(setType fnc) : set(fnc) {  }
    Property(setType fncSet, getType fncGet) : get(fncGet), set(fncSet) {  }

    Property<T> &operator=(T& value) { set(value); return *this; }

    operator const T&() const { return get(); }
};

int main(void)
{
    Property<int> hey(12);

    std::cout << hey << std::endl;

    return hey;
}

It seems to behave correctly under visual studio compiler but i'm unsure.

Have I missed a part of the standard? Is my code incorrect? is there a bug in clang / gcc / the STL?

I placed my code on the website called Godbolt to switch easily between compilers and I had the same incoherent results.

Here is what it prints me for c 14 / clang 14:

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
624203080

The last number changes between runs, but not the first one making me think it just takes uninitialized data.

CodePudding user response:

Your problem is this part: using getType = std::function<const T&(void)>; in combination with get{[this] () { return internal; }}.

The lambda does not return the internal as a reference here, so a copy of internal is returned, and - here I don't know how std::function is implemented - std::function has to hold a copy of internal but returns it as a reference to that copy which is than dangling.

Changing it to get{[this] () -> T& { return internal; }} seems to solve the problem.

CodePudding user response:

The lambda you initialise get with returns by value, not by reference, which means that the returned int ceases to exist somewhere in the std::function machinery.

You can specify a reference return

[this] () -> const T& { return internal; }

N.b. you should make Property uncopyable and immovable, as you can't re-point the this capture within the std::function members when copying or moving.

  •  Tags:  
  • c
  • Related