llvm / llvm/llvm-project

[ C++ 23] Confusing behavior when temporaries are provided to a function that is part of a range loop initializer.

Open
#171,630 3 comments 0 reactions 0 assignees View on GitHub
clang:frontend diverges-from:gcc question
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

https://godbolt.org/z/TT8xM4eYe

When providing temporaries into a function that is used as the for-range-initializer, the temporaries are destroyed before the end of the full expression. Sample code can be found below with a live example in compiler explorer. This only happens when the function arguments for `foo` are values. When the function arguments of `foo` are references (l-value / r-value), clang's output matches the one from gcc below.

```cpp
struct Foo
{
int num = 0;

Foo() { std::cout << "Constructed!\n"; }
~Foo() { std::cout << "Destructed!\n"; }

Foo(const Foo&) = default;
Foo(Foo&&) = default;

auto operator=(const Foo&) -> Foo& = default;
auto operator=(Foo&&) -> Foo& = default;
};

struct JankStringView : std::string_view
{
using super = std::string_view;

using super::super;

~JankStringView() { std::cout << "string view is destroyed\n"; }
};

struct JankString : std::string
{
using super = std::string;

using super::super;

~JankString() { std::cout << "string is destroyed\n"; }
};

auto foo([[maybe_unused]] Foo f, [[maybe_unused]] JankStringView view) -> std::vector
{
std::cout << view.data() << "\n";
std::cout << "Exiting coroutine\n";

return { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
};

auto main() -> int
{
for (auto x : foo(Foo{}, JankStringView{ JankString{ "Hello" }.c_str() }))
{
std::cout << "x: " << x << "\n";
}

return 0;
}
```
On Clang (trunk), the output is:
```
Constructed!
Hello
Exiting coroutine
string view is destroyed
Destructed!
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
x: 10
string is destroyed
```
On GCC (trunk) however, the output is more aligned with the wording that's in the [standard](https://eel.is/c++draft/class.temporary#4).
```
Constructed!
Hello
Exiting coroutine
x: 1
x: 2
x: 3
x: 4
x: 5
x: 6
x: 7
x: 8
x: 9
x: 10
string view is destroyed
string is destroyed
Destructed!
```

Contributor guide

Open the contributing guide

Research direction

Start with the Compiler Explorer reproducer at https://godbolt.org/z/TT8xM4eYe and read the linked standard wording on temporary object lifetimes. Trace Clang's C++23 range-for handling and compare its result with GCC; done means the temporaries remain alive through the loop as required by the standard.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.