I want to use std::aligned_storage_t
to decouple allocation and construction, so I write two little helpers to help me do this
#include <type_traits>
template <typename T>
using Uninitialized = std::aligned_storage_t<sizeof(T), alignof(T)>;
template <typename T, typename... Args>
auto get_initialized(Uninitialized<T>& block, Args... args) -> T&
requires(
// If we allow type with default constructor, then it would be ambiguous
// if the caller wants initialization or simply getting reference.
!std::is_default_constructible_v<T> &&
(sizeof...(Args) == 0 || std::is_constructible_v<T, Args...>)
)
{
T* p = (T*) █
if constexpr (sizeof...(Args) > 0)
new (p) T{args...};
return *p;
}
And I tested it with
class Obj {
public:
Obj() = delete;
Obj(const char* s) noexcept : m_str(s) {}
void print() noexcept {
std::cout << m_str << std::endl;
}
private:
const char* m_str = nullptr;
};
int main() {
Uninitialized<Obj> obj{};
const char* s = "Test";
Obj& o = get_initialized(obj, s);
o.print();
}
And it failed. I've tried clang, gcc and msvc, and they all complained about "no matching function call". Complaint from clang
<source>:37:14: error: no matching function for call to 'get_initialized'
Obj& o = get_initialized(obj, s);
^~~~~~~~~~~~~~~
Of course, simply add <Obj>
the code would work fine. But I just want to save some typing here. I found output from gcc providing some insight here
<source>:37:29: error: no matching function for call to 'get_initialized(Uninitialized<Obj>&, const char*&)'
37 | Obj& o = get_initialized(obj, s);
| ~~~~~~~~~~~~~~~^~~~~~~~
So I guess the problem relates to the type of the parameters of constructor, and I'm also quite curious here why would the type of s
would be deduced to char*&
while it's just a 8 byte data and can be copied without extra cost. So is there a way to enforce the parameter to be copied, just like std::ref
or std::cref
?
Uninitialized<T>
is an alias...
So your template is actually
using Uninitialized = ;
template <typename T, typename... Args>
auto get_initialized(std::aligned_storage_t<sizeof(T), alignof(T)>& block, Args... args) -> T&
T
is not deducible here (any class with same sizeof
/alignof
would be valid).
See Deduction_from_a_type for more info