From b8395c6d9f185ccf55868032c2b9090bac88d9e8 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 21 Jan 2025 11:01:22 -0500 Subject: [PATCH] Got a test case working that shows emplace_back and push_back do the same function calls in every instance except for one. --- Makefile | 3 +- emplace_move/test.cpp | 121 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 156120a..6e401d4 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,7 @@ tracy_build: meson compile -j 10 -C builddir test: build - ./builddir/emplace_test.exe - ./builddir/rvo_test.exe + ./builddir/emplace_test.exe "[proof]" clean: meson compile --clean -C builddir diff --git a/emplace_move/test.cpp b/emplace_move/test.cpp index dee87ae..9a49736 100644 --- a/emplace_move/test.cpp +++ b/emplace_move/test.cpp @@ -7,34 +7,88 @@ using namespace fmt; +struct Counts { + int default_constructor = 0; + int value_constructor = 0; + int copy_constructor = 0; + int move_constructor = 0; + int move_assign = 0; + int copy_assign = 0; + int destructor = 0; + + void reset() { + default_constructor = 0; + value_constructor = 0; + copy_constructor = 0; + move_constructor = 0; + move_assign = 0; + copy_assign = 0; + destructor = 0; + } + + int totals() { + return default_constructor + + value_constructor + + copy_constructor + + move_constructor + + move_assign + + copy_assign + + destructor; + } + + void dump() { + fmt::println("default_constructor {} value_constructor {} copy_constructor {} move_constructor {} move_assign {} copy_assign", + default_constructor, + value_constructor, + copy_constructor, + move_constructor, + move_assign, + copy_assign, + destructor); + } +}; + +Counts COUNTS; + // Some heavy object struct A { - A() = default; + int val = 0; + + A() { + COUNTS.default_constructor++; + std::cout << "default called constructor with: " << val << std::endl; + } A(int val) : val(val) { + COUNTS.value_constructor++; std::cout << "called constructor with: " << val << std::endl; } A(A const& other) { + COUNTS.copy_constructor++; std::cout << "calling copy(A const& other): " << val << std::endl;; } A(A&& other) { + COUNTS.move_constructor++; std::cout << "calling move(A&& other): " << val << std::endl;; } A& operator=(A&& other) { + COUNTS.move_assign++; std::cout << "calling move(A&& other)=: " << val << std::endl;; return *this; } A& operator=(A const& other) { + COUNTS.copy_assign++; std::cout << "calling copy(A const& other)=: " << val << std::endl;; return *this; } - ~A() = default; - - int val{}; + ~A() { + COUNTS.destructor++; + std::cout << "destructor called" << std::endl; + } }; @@ -92,9 +146,68 @@ TEST_CASE("pushback move tests", "[push_back]") { // to put the value in a slot so the compiler had to call the move constructor std::cout << "== 4. Push Back: Calling move constructor\n"; test.push_back(A{}); + } TEST_CASE("proof you don't need emplace or std::move", "[proof]") { + A a{20}; + A b{30}; + COUNTS.reset(); + + std::vector emtest; + emtest.reserve(10); + + std::vector pbtest; + pbtest.reserve(10); + REQUIRE(COUNTS.totals() == 0); + + emtest.emplace_back(a); + REQUIRE(COUNTS.totals() == 1); + COUNTS.dump(); + COUNTS.reset(); + + pbtest.push_back(b); + REQUIRE(COUNTS.totals() == 1); + COUNTS.dump(); + COUNTS.reset(); + + // confirm these do the same number of calls + emtest.emplace_back(A{}); + REQUIRE(COUNTS.totals() == 3); + COUNTS.dump(); + COUNTS.reset(); + + pbtest.push_back(A{}); + REQUIRE(COUNTS.totals() == 3); + COUNTS.dump(); + COUNTS.reset(); + + { + REQUIRE(COUNTS.totals() == 0); + + A c{}; + emtest.emplace_back(std::move(c)); + REQUIRE(COUNTS.totals() == 2); + COUNTS.dump(); + COUNTS.reset(); + + A d{}; + pbtest.push_back(std::move(d)); + REQUIRE(COUNTS.totals() == 2); + COUNTS.dump(); + COUNTS.reset(); + } + + COUNTS.reset(); + + emtest.emplace_back(200); + REQUIRE(COUNTS.totals() == 1); + COUNTS.dump(); + COUNTS.reset(); + pbtest.push_back(200); + REQUIRE(COUNTS.totals() == 3); + COUNTS.dump(); + COUNTS.reset(); }