https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121946
Bug ID: 121946 Summary: [missing optimization] std::vector does not use memcpy for the trivially copyable/destructible type Product: gcc Version: 15.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: dmitriy.ovdienko at gmail dot com Target Milestone: --- I have a class which is trivially copyable, moveable and destructible. It is pretty much similar to the `int` type. #include <vector> template<typename T, T EmptyValue> struct my_optional { T value_ {EmptyValue}; my_optional() = default; my_optional(T value) : value_(value) {} }; void foo_my_optional(std::vector<my_optional<int, -1>>& v) { v.push_back(1); } void foo_int(std::vector<int>& v) { v.push_back(1); } My goal is to implement it in such way, so it follows the zero-overhead principle. In fact, when I compare the assembly generated for the std::vector<int>, there is a difference. For the std::vector<int> I see compiler uses memcpy during the reallocation while for the std::vector<my_optional> it does not. Instead, it generates some loop. It seems the issue is std::uninitialized_copy function. For some reason in order to use memcpy it requires the class to be trivially_constructible: if constexpr (!__is_trivially_constructible(_ValT, decltype(*__first))) return std::__do_uninit_copy(__first, __last, __result); else if constexpr (__memcpyable<_Dest, _Src>::__value) { ptrdiff_t __n = __last - __first; if (__n > 0) [[__likely__]] { using _ValT = typename remove_pointer<_Src>::type; __builtin_memcpy(std::__niter_base(__result), std::__niter_base(__first), __n * sizeof(_ValT)); __result += __n; } return __result; } ... while from my perspective it is enough to be trivially destructible and trivially copyable/moveable. Link to the godbolt.org: https://godbolt.org/z/cx9PqfqGv