The example below does not crash, but prints nothing with MSVC Compiler Version 19.32.31332 and prints "def" with GCC:
#include <string>
#include <vector>
#include <set>
#include <ranges>
#include <iostream>
template <class R, class Value>
concept range_over = std::ranges::range<R> &&
std::same_as<std::ranges::range_value_t<R>, Value>;
const std::string& find1(const std::vector<std::string>& v)
{
return v[1];
}
template <range_over<std::string> Range>
std::reference_wrapper<std::string> find2(Range range)
{
return *(range.begin() 1);
}
int main()
{
std::vector<std::string> v = { "abc", "def", "ghi" };
//find1 always prints "def"
//std::cout << find1(v);
std::cout << find2(v).get() << std::endl;
return 0;
}
But in my real-life app a similar code crashes with MSVC Compiler Version 19.32.31332.
Is the code correct? Is v
valid after func2(v)
call?
CodePudding user response:
find2
takes the range by-value. So you are returning a reference into the function parameter object, which is a copy of the vector v
in main
, which in itself is very likely not the intention of the function. E.g. modifications through the returned reference would not be reflected in v
.
It is implementation-defined whether function parameters are destroyed when the function returns or after the full-expression containing the function call. Practically speaking this will be determined by the C ABI used (which also potentially explains different behavior between MSVC and GCC/Clang).
So, depending on how the implementation used defines this, it may or may not have undefined behavior. If the parameter object is destroyed when the function returns, the call to operator<<
will read a value through a dangling reference. Otherwise it is a valid program and will print def
.
Before C 17 the standard said that function parameter objects are always destroyed when the function returns. This was changed with CWG issue 1880. As the issue notes however, actual implementations/ABIs did have the (at the time) non-conforming behavior of destroying them at the end of the full-expression and the implementation-definedness in the resolution was chosen so that these ABIs wouldn't break. So practically speaking the standard just adjusted to actual practice.