Asteroids With Wings
Asteroids With Wings

Reputation: 17454

Is this reinterpret_cast problematic in principle, and/or in practice?

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

Answers (3)

Asteroids With Wings
Asteroids With Wings

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 class X is called for an object that is not of type X, or of a type derived from X, 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

MSalters
MSalters

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

Nicol Bolas
Nicol Bolas

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

Related Questions