Reputation: 17454
Take the following:
#include <vector>
#include <string>
#include <type_traits>
int main()
{
std::vector<std::string> vec{"foo", "bar"};
for (auto& el : vec)
el.std::string::~string();
auto& aliased = reinterpret_cast<
std::vector<
std::aligned_storage_t<sizeof(std::string), alignof(std::string)>
>&>(vec);
aliased.clear();
}
(whittled down from more complex code, of course — we wouldn't generally manage a simple std::string
vector this way in such a simple testcase)
Does this program have undefined behaviour? I thought that we cannot alias vector<T1>
as vector<T2>
, even if T1
and T2
are compatible.
And if so, can this be expected to have practical ramifications at runtime?
Assume strict aliasing is not disabled in the compiler.
Interestingly, GCC 9.2.0 doesn't give me any warnings with -fstrict-aliasing -Wstrict-aliasing
(live demo).
Upvotes: 4
Views: 144
Reputation: 17454
My conclusion from the answers and comments so far (some of which have been deleted ☹️) is that, although strict aliasing is defined in terms of accesses and therefore only applies to scalar types (which the current draft makes more clear), the program in question is still assuredly a case of undefined behaviour:
[class.mfct.non-static]/2
: If a non-static member function of a classX
is called for an object that is not of typeX
, or of a type derived fromX
, the behavior is undefined.
And that, therefore, even if the compiler decides not to remove the clear()
statement (or otherwise do "weird" things), we could not guarantee that the layout and operation of the target vector
specialisation matches that of the original type, and that this pattern should therefore be avoided in production code in general.
So, whichever way you spin it, this is problematic in principle, and potentially problematic in practice too.
(All references are to n4659, which is basically C++17.)
Upvotes: 1
Reputation: 179819
I think walnut has roughly the correct answer.
Technically, accessing an object of class type is broken down in one or more accesses of scalar objects in the class. All the scalar accesses individually must obey the aliasing rules.
This is important for C compatibility. In C, struct Foo { int a; }
and struct Bar { int b; }
may alias eachother.
In this case, it's Undefined Behavior because the standard does not define the scalar members of std::string
or std::vector
. std::aligned_storage_t
might be an array of scalars, but that's not guaranteed either.
Upvotes: 1
Reputation: 473437
Does this program have undefined behaviour?
Absolutely. You are accessing an object of type vector<string>
through a reference to some unrelated type. That's UB, a violation of the strict aliasing rule.
And if so, can this be expected to have practical ramifications at runtime?
UB means that the behavior of the runtime is undefined. So yeah.
Upvotes: 9