Guaranteed copy elision and GNU ?: operator

Hello,

I recently came across an issue when using the gnu conditional
operator (?:slight_smile: in C++17. In C++14, the code below when compiled with
Clang 5 outputs 42 as expected, but in C++17 the output is 0. In
C++17, I believe the copy constructor for shared_ptr is being elided
and so the ref count is not incremented and the underlying ptr gets
deleted. If the first operand is cast to an rvalue (e.g.,
std::move(makeSharedIntWrapper(42,true)), the behavior is as expected
(since we are disabling the copy elision). Since this is an GNU
extension, is it OK to not elide the copy in this case?

I didn't test to see what gcc does since I don't have ready access to gcc7.

#include <memory>
#include <iostream>

struct int_wrapper {
    int_wrapper(const int& x) : x(new int(x)) {}
    ~int_wrapper() { delete x; }
    int *x;
};

static std::shared_ptr<int_wrapper> makeSharedIntWrapper(int x, bool t) {
    return t ? std::make_shared<int_wrapper>(x) :
std::shared_ptr<int_wrapper>();
}

int main(int argc, const char * argv) {
    auto s = makeSharedIntWrapper(42, true) ?:
std::make_shared<int_wrapper>(17);
    std::cout << *s.get()->x << std::endl;

    return 0;
}

Hello,

I recently came across an issue when using the gnu conditional
operator (?:slight_smile: in C++17. In C++14, the code below when compiled with
Clang 5 outputs 42 as expected, but in C++17 the output is 0. In
C++17, I believe the copy constructor for shared_ptr is being elided
and so the ref count is not incremented and the underlying ptr gets
deleted. If the first operand is cast to an rvalue (e.g.,
std::move(makeSharedIntWrapper(42,true)), the behavior is as expected
(since we are disabling the copy elision). Since this is an GNU
extension, is it OK to not elide the copy in this case?

I didn't test to see what gcc does since I don't have ready access to gcc7.

I was able to confirm that gcc7 has the same behavior as above.

[+Richard]

Hello,

I recently came across an issue when using the gnu conditional
operator (?:slight_smile: in C++17. In C++14, the code below when compiled with
Clang 5 outputs 42 as expected, but in C++17 the output is 0. In
C++17, I believe the copy constructor for shared_ptr is being elided
and so the ref count is not incremented and the underlying ptr gets
deleted. If the first operand is cast to an rvalue (e.g.,
std::move(makeSharedIntWrapper(42,true)), the behavior is as expected
(since we are disabling the copy elision). Since this is an GNU
extension, is it OK to not elide the copy in this case?

I didn't test to see what gcc does since I don't have ready access to gcc7.

I was able to confirm that gcc7 has the same behavior as above.

You mean that it prints 0 or 42?

  -Hal

[+Richard]

Hello,

I recently came across an issue when using the gnu conditional
operator (?:slight_smile: in C++17. In C++14, the code below when compiled with
Clang 5 outputs 42 as expected, but in C++17 the output is 0. In
C++17, I believe the copy constructor for shared_ptr is being elided
and so the ref count is not incremented and the underlying ptr gets
deleted. If the first operand is cast to an rvalue (e.g.,
std::move(makeSharedIntWrapper(42,true)), the behavior is as expected
(since we are disabling the copy elision). Since this is an GNU
extension, is it OK to not elide the copy in this case?

I didn't test to see what gcc does since I don't have ready access to
gcc7.

I was able to confirm that gcc7 has the same behavior as above.

You mean that it prints 0 or 42?

It prints 42 when compiled with c++14 and 0 with c++17 (c++1z).

I also submitted this with a simpler case as
https://bugs.llvm.org/show_bug.cgi?id=35039

Ron

[+Richard]

Hello,

I recently came across an issue when using the gnu conditional
operator (?:slight_smile: in C++17. In C++14, the code below when compiled with
Clang 5 outputs 42 as expected, but in C++17 the output is 0. In
C++17, I believe the copy constructor for shared_ptr is being elided
and so the ref count is not incremented and the underlying ptr gets
deleted. If the first operand is cast to an rvalue (e.g.,
std::move(makeSharedIntWrapper(42,true)), the behavior is as expected
(since we are disabling the copy elision). Since this is an GNU
extension, is it OK to not elide the copy in this case?

I didn't test to see what gcc does since I don't have ready access to
gcc7.

I was able to confirm that gcc7 has the same behavior as above.

You mean that it prints 0 or 42?

It prints 42 when compiled with c++14 and 0 with c++17 (c++1z).

I also submitted this with a simpler case as
https://bugs.llvm.org/show_bug.cgi?id=35039

To ask the obvious question, given that this is a GNU extension, has this been raised with the GCC developers? I imagine that we do want to match GCC's behavior in this regard.

  -Hal

It prints 42 when compiled with c++14 and 0 with c++17 (c++1z).

I also submitted this with a simpler case as
https://bugs.llvm.org/show_bug.cgi?id=35039

To ask the obvious question, given that this is a GNU extension, has this
been raised with the GCC developers? I imagine that we do want to match
GCC's behavior in this regard.

Argh! This is embarrassing. I'm on a Mac and when I installed gcc I forgot
that `g++` is sym-linked to clang.

It appears that gcc never allowed this code to compile to begin with. See
godbolt: Compiler Explorer

So I guess clang should give an error here too.

It prints 42 when compiled with c++14 and 0 with c++17 (c++1z).

I also submitted this with a simpler case as
https://bugs.llvm.org/show_bug.cgi?id=35039

To ask the obvious question, given that this is a GNU extension, has this
been raised with the GCC developers? I imagine that we do want to match
GCC's behavior in this regard.

Argh! This is embarrassing. I'm on a Mac and when I installed gcc I forgot
that `g++` is sym-linked to clang.

It appears that gcc never allowed this code to compile to begin with. See
godbolt: Compiler Explorer

So I guess clang should give an error here too.

Ah, okay. Please update your bug report as well.

  -Hal

It prints 42 when compiled with c++14 and 0 with c++17 (c++1z).

I also submitted this with a simpler case as
https://bugs.llvm.org/show_bug.cgi?id=35039

To ask the obvious question, given that this is a GNU extension, has this
been raised with the GCC developers? I imagine that we do want to match
GCC’s behavior in this regard.

Argh! This is embarrassing. I’m on a Mac and when I installed gcc I forgot
that g++ is sym-linked to clang.

It appears that gcc never allowed this code to compile to begin with. See
godbolt: https://godbolt.org/g/8DdytF

So I guess clang should give an error here too.

Ah, okay. Please update your bug report as well.

There are several GCC extensions that we intentionally support in C++ mode even when GCC does not, and IIRC ?: is one of them.

We cannot perform any sort of copy/move elision on the result of ?: because of the rules around the order of destruction of temporaries. We can of course at least attempt to move it. It is likely that there is some piece of code which fails to understand this correctly in C++17 mode.

John.