Use of placement new in expected_operations_base.
swan-gh opened this issue · comments
Hello TL :)
This might be a non-issue, it's likely I've overlooked something, but it would appear that the usage of placement new here:
template <class... Args> void construct(Args &&... args) noexcept {
new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
this->m_has_val = true;
}
template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
this->m_has_val = true;
}
begins to misbehave in some cases when constructing an expected<T const>
from an expected<T>
. std::addressof
returns a pointer of type T const *
which seems to confuse the overload resolution of operator new. E.g.
#include "expected.hpp"
#include <iostream>
class A
{
public:
A() = default;;
A(A const &) = default;
A(A&&) = default;
~A() {}
A& operator=(A const &) = default;
A& operator=(A &&) = default;
int i = 5u;
};
template< typename T>
using Expected = tl::expected<T, int>;
Expected<A> getSomeExpected()
{
return Expected<A>(A{});
}
int main()
{
Expected<A const> a (getSomeExpected());
}
Gives the following:
1> error C2665: 'operator new': none of the 4 overloads could convert all the argument types
...
1> expectedtest\expected.hpp(665): note: while trying to match the argument list '(unsigned __int64, _Ty *)'
1> with
1> [
1> _Ty=const A
1> ]
Casting the address type passed to placement new works, although I suspect that's probably UB.
Compiled with:
MSVC++ 14.16, _MSC_VER == 1916 (Visual Studio 2017 version 15.9)
/std:c++17
I think the solution might be to use something similar to std::construct_at
from C++20?
https://en.cppreference.com/w/cpp/memory/construct_at
equivalent to
return ::new (const_cast<void*>(static_cast<const volatile void*>(p)))
T(std::forward<Args>(args)...);
Hey just bumping this because I ran into a similar issue, this time with using expected
to store types with a delete
d placement new
operator. In my case this works fine in MSVC with C++23 std::expected
, and I see that they indeed use std::construct_at
for all their member construction.