diff --git a/Code/Framework/AzCore/AzCore/std/createdestroy.h b/Code/Framework/AzCore/AzCore/std/createdestroy.h index 3c60266562..94b86a58e4 100644 --- a/Code/Framework/AzCore/AzCore/std/createdestroy.h +++ b/Code/Framework/AzCore/AzCore/std/createdestroy.h @@ -21,7 +21,18 @@ namespace AZStd { // alias std::pointer_traits into the AZStd::namespace using std::pointer_traits; + + //! Bring the names of uninitialized_default_construct and + //! uninitialized_default_construct_n into the AZStd namespace + using std::uninitialized_default_construct; + using std::uninitialized_default_construct_n; + + //! uninitialized_value_construct and uninitialized_value_construct_n + //! are now brought into scope of the AZStd namespace + using std::uninitialized_value_construct; + using std::uninitialized_value_construct_n; } + namespace AZStd::Internal { template @@ -223,107 +234,6 @@ namespace AZStd } } -namespace AZStd -{ - //! C++20 implementation of uninitialized_default_construct - //! Initializes objects by default-initialization via placement new - //! Ex. `new(declval()) T` - Notice no parenthesis after T - //! This performs default initialization instead of value initialization - //! Default initialization performs the following actions - //! # If T is a class type it considers constructors which can be invoked - //! with an empty argument list. The selected constructor is invoked - //! to provide the initial value of the object - //! # If T is an array type, then default initialization is performed - //! on each array element - //! # Otherwise nothing is done and objects with automatic storage duration(i.e scope) - //! are initialized with indeterminate values - //! For example given the following struct - //! struct Foo - //! { - //! int mint; - //! double bubble; - //! }; - //! Invoking uninitialized_default_construct(FooPtr, FooPtr + 1) - //! Will default initialize the FooPtr object (Foo has an implicitly-defined default constructor) - //! The values of mint and bubble are indeterminate - template - constexpr auto uninitialized_default_construct(ForwardIt first, ForwardIt last) - -> enable_if_t, void> - { - for (; first != last; ++first) - { - return ::new (AZStd::addressof(*first)) typename AZStd::iterator_traits::value_type; - } - } - // C++20 implementation of uninitialized_default_construct_n - // Constructs "n" objects starting at first via default-initialization - template - constexpr auto uninitialized_default_construct_n(ForwardIt first, Size numElements) - -> enable_if_t, ForwardIt> - { - for (; numElements > 0; ++first, --numElements) - { - return ::new (AZStd::addressof(*first)) typename AZStd::iterator_traits::value_type; - } - - return first; - } -} - -namespace AZStd -{ - //! C++20 implementation of uninitialized_value_construct - //! Initializes objects by value-initialization via placement new - //! Ex. `new(declval()) T()` - Notice parenthesis are here after T - //! value-initialization of an object performs different rules depending - //! on the type of T - //! Value initialization performs the following actions - //! # If T is a class type with no default constructor or with a user-provided - //! constructor or a deleted default constructor, then default-initialization - //! is performed - //! # If T is a class type with a default constructor that is neither - //! user-provided nor deleted(i.e a class with an implicitly-defined or defaulted - //! default constructor), then the object is zero-initialized and then it is - //! default-initialized if it has a non-trivial default constructor - //! # If T is an array type, then value initialization is performed - //! on each array element - //! # Otherwise the object is zero-initialized - //! (i.e sets arithmetic and enum objects to 0, bool objects to false, pointers to nullptr) - //! For example given the following struct - //! struct Foo - //! { - //! int mint; - //! double bubble; - //! }; - //! Invoking uninitialized_default_construct(FooPtr, FooPtr + 1) - //! Will value-initialize the FooPtr object. - //! The Foo has an implicitly-defined default constructor. - //! For aggregates such as int and double this will perform zero-initialization - //! which will set their values to 0 - //! Therefore The values of mint will be 0 and and bubble 0.0 - template - constexpr auto uninitialized_value_construct(ForwardIt first, ForwardIt last) - -> enable_if_t, void> - { - for (; first != last; ++first) - { - return ::new (AZStd::addressof(*first)) typename AZStd::iterator_traits::value_type(); - } - } - // C++20 implementation of uninitialized_default_construct_n - // Constructs "n" objects starting at the first via by value-initialization - template - constexpr auto uninitialized_value_construct_n(ForwardIt first, Size numElements) - -> enable_if_t, ForwardIt> - { - for (; numElements > 0; ++first, --numElements) - { - return ::new (AZStd::addressof(*first)) typename AZStd::iterator_traits::value_type(); - } - - return first; - } -} namespace AZStd::Internal { diff --git a/Code/Framework/AzCore/Tests/AZStd/CreateDestroy.cpp b/Code/Framework/AzCore/Tests/AZStd/CreateDestroy.cpp index 9ccd1b0742..f9c72f6260 100644 --- a/Code/Framework/AzCore/Tests/AZStd/CreateDestroy.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/CreateDestroy.cpp @@ -134,4 +134,48 @@ namespace UnitTest EXPECT_FLOAT_EQ(4.0f, resultAddress->m_floatValue); AZStd::destroy_at(resultAddress); } + + TEST(CreateDestroy, UninitializedDefaultConstruct_IsAbleToConstructMultipleElements_Succeeds) + { + struct RefWrapper + { + RefWrapper() + {} + int m_intValue{ 2 }; + }; + constexpr size_t ArraySize = 2; + AZStd::aligned_storage_for_t testArray[ArraySize]; + RefWrapper(&uninitializedAddress)[2] = reinterpret_cast(testArray); + AZStd::uninitialized_default_construct(AZStd::begin(uninitializedAddress), AZStd::end(uninitializedAddress)); + + + EXPECT_EQ(2, uninitializedAddress[0].m_intValue); + EXPECT_EQ(2, uninitializedAddress[1].m_intValue); + // Reset uninitializedAddress to Debug pattern + memset(uninitializedAddress, 0xCD, ArraySize * sizeof(RefWrapper)); + AZStd::uninitialized_default_construct_n(AZStd::data(uninitializedAddress), AZStd::size(uninitializedAddress)); + EXPECT_EQ(2, uninitializedAddress[0].m_intValue); + EXPECT_EQ(2, uninitializedAddress[1].m_intValue); + } + + TEST(CreateDestroy, UninitializedValueConstruct_IsAbleToConstructMultipleElements_Succeeds) + { + struct RefWrapper + { + int m_intValue; + }; + constexpr size_t ArraySize = 2; + AZStd::aligned_storage_for_t testArray[ArraySize]; + RefWrapper(&uninitializedAddress)[2] = reinterpret_cast(testArray); + AZStd::uninitialized_value_construct(AZStd::begin(uninitializedAddress), AZStd::end(uninitializedAddress)); + + + EXPECT_EQ(0, uninitializedAddress[0].m_intValue); + EXPECT_EQ(0, uninitializedAddress[1].m_intValue); + // Reset uninitializedAddress to Debug pattern + memset(uninitializedAddress, 0xCD, ArraySize * sizeof(RefWrapper)); + AZStd::uninitialized_value_construct_n(AZStd::data(uninitializedAddress), AZStd::size(uninitializedAddress)); + EXPECT_EQ(0, uninitializedAddress[0].m_intValue); + EXPECT_EQ(0, uninitializedAddress[1].m_intValue); + } }