ARTICLE AD BOX
It is possible to design non-constructible classes, but I managed to instantiate an object of such a class:
#include <bit> #include <cstring> #include <iostream> // Not constructible // Not implicit lifetime struct Test { int val{1}; Test() = delete; Test(Test const&) = delete; Test(Test&&) = delete; Test& operator=(Test const&) = default; Test& operator=(Test&&) = default; ~Test() = default; operator int() { return val; } }; int main() { char storage[sizeof(Test)]; int i{666}; std::memcpy(storage,&i,sizeof(Test)); auto HeWhoMustNotBeNamed = std::bit_cast<Test>(storage); std::cout << HeWhoMustNotBeNamed.val; }All tested compilers (gcc, clang and msvc) recognize Test as trivially copyable.
Test and storage fulfill the preconditions of std::bit_cast, therefore clang and msvc create an object of a non-constructible class, without undefined behavior.
Only gcc does not compile, trying to use the deleted move constructor for returning the result. I assume that msvc and clang do some copy elision from std::bit_cast while gcc does not.
In my opinion, such behavior is undesirable as it violates the class semantic. I think that the issue lies in the definition of trivially copyable, which allows for classes that cannot have instances (though it's a syllogism that all objects that cannot exist can be trivially copied).
Is there something wrong with my analysis?
Note: the question is not entirely theoretical as the non-constructible class can be a base implementation class that must be used only as inherited:
// Not constructible // Not implicit lifetime struct BaseImpl { int val{1}; BaseImpl() = delete; BaseImpl(BaseImpl const&) = delete; BaseImpl(BaseImpl&&) = delete; BaseImpl& operator=(BaseImpl const&) = default; BaseImpl& operator=(BaseImpl&&) = default; ~BaseImpl() = default; operator int() { return val; } protected: BaseImpl(int i) : val{i} {}; }; struct Concrete: private BaseImpl { Concrete(int i) : BaseImpl{i}{}; int Value() const {return BaseImpl::val;}; };