From ee2e33507221ee3ab4a1be43442844461b892b3d Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Fri, 10 Apr 2020 07:45:15 +0200
Subject: [PATCH] do not copy the variant in its visitor

---
 alib2std/src/extensions/container/variant.hpp | 35 ++++----
 .../extensions/container/VariantTest.cpp      | 82 +++++++++++++++++++
 2 files changed, 101 insertions(+), 16 deletions(-)

diff --git a/alib2std/src/extensions/container/variant.hpp b/alib2std/src/extensions/container/variant.hpp
index c01ddbe70a..8c8194e7f3 100644
--- a/alib2std/src/extensions/container/variant.hpp
+++ b/alib2std/src/extensions/container/variant.hpp
@@ -40,21 +40,7 @@ namespace ext {
 
 template < class Visitor, class... Variants >
 constexpr auto visit ( Visitor && vis, Variants && ... vars ) {
-	auto extToStd = [] ( auto && variant ) {
-		using __std__variant = typename std::decay_t < decltype ( variant ) >::__base;
-
-		if constexpr ( std::is_lvalue_reference_v < decltype ( variant ) > && std::is_const_v < std::remove_reference_t < decltype ( variant ) > > ) {
-			return static_cast < const __std__variant & > ( variant );
-		} else if constexpr ( std::is_lvalue_reference_v < decltype ( variant ) > ) {
-			return static_cast < __std__variant & > ( variant );
-		} else if constexpr ( std::is_rvalue_reference_v < decltype ( variant ) > ) {
-			return static_cast < __std__variant && > ( variant );
-		} else {
-			static_assert ( "Unhandled option" );
-		}
-	};
-
-	return std::visit ( std::forward < Visitor > ( vis ), extToStd ( std::forward < Variants > ( vars ) ) ... );
+	return std::visit ( std::forward < Visitor > ( vis ), std::forward < Variants > ( vars ).asStdVariant ( ) ... );
 }
 
 /**
@@ -107,6 +93,10 @@ using __std__variant = typename variant_builder_start < Ts ... >::__std__variant
  */
 template < typename ... Ts >
 class variant : public __std__variant < Ts ... > {
+
+	template < class Visitor, class... Variants >
+	friend constexpr auto visit ( Visitor && vis, Variants && ... vars );
+
 	/**
 	 * Internal variant to string callback implementation.
 	 */
@@ -178,12 +168,25 @@ class variant : public __std__variant < Ts ... > {
 			return - second.compare ( first );
 		}
 	};
-public:
+
 	/**
 	 * Remember the base class, i.e. std::variant
 	 */
 	typedef __std__variant < Ts ... > __base;
 
+	const __base & asStdVariant ( ) const & {
+		return * this;
+	}
+
+	__base & asStdVariant ( ) & {
+		return * this;
+	}
+
+	__base && asStdVariant ( ) && {
+		return std::move ( * this );
+	}
+
+public:
 	/**
 	 * Inherit constructors of the standard variant
 	 */
diff --git a/alib2std/test-src/extensions/container/VariantTest.cpp b/alib2std/test-src/extensions/container/VariantTest.cpp
index 4294e1edf7..3d44695bc9 100644
--- a/alib2std/test-src/extensions/container/VariantTest.cpp
+++ b/alib2std/test-src/extensions/container/VariantTest.cpp
@@ -68,6 +68,51 @@ namespace {
 
 		test2(int i) : m_i(i) {}
 	};
+
+	class Moveable {
+		int& m_moves;
+		int& m_copies;
+
+		public:
+		Moveable(int& moves, int& copies) : m_moves(moves), m_copies(copies) {
+			m_moves = 0;
+			m_copies = 0;
+		}
+
+		Moveable(const Moveable& src) : m_moves(src.m_moves), m_copies(src.m_copies) {
+			m_copies++;
+		}
+
+		Moveable(Moveable&& src) : m_moves(src.m_moves), m_copies(src.m_copies) {
+			m_moves++;
+		}
+
+		Moveable & operator = ( const Moveable & ) {
+			m_copies ++;
+			return * this;
+		}
+
+		Moveable & operator = ( Moveable && ) {
+			m_moves ++;
+			return * this;
+		}
+
+		bool operator<(const Moveable&) const {
+			return false;
+		}
+
+		bool operator==(const Moveable&) const {
+			return true;
+		}
+
+		bool operator!=(const Moveable&) const {
+			return false;
+		}
+
+		friend std::ostream & operator << ( std::ostream & os, const Moveable & ) {
+			return os << "Moveable";
+		}
+	};
 }
 
 namespace ext {
@@ -79,6 +124,13 @@ namespace ext {
 			}
 		};
 
+	template<>
+		struct compare<Moveable> {
+			int operator()(const Moveable& , const Moveable& ) {
+				return 0;
+			}
+		};
+
 } /* namespace ext */
 
 TEST_CASE ( "Variant", "[unit][std][container]" ) {
@@ -232,4 +284,34 @@ TEST_CASE ( "Variant", "[unit][std][container]" ) {
 		v = std::move ( v2 );
 		v.get < std::set < int > > ( ).insert ( 1 );
 	}
+
+	SECTION ( "Copy vs. Move" ) {
+		int moves = 0;
+		int copies = 0;
+
+		ext::variant < Moveable, int > inst1 ( Moveable ( moves, copies ) );
+
+		CHECK ( moves == 1 );
+		CHECK ( copies == 0 );
+
+		ext::variant < Moveable, int > inst2 ( 1 );
+
+		CHECK ( moves == 1 );
+		CHECK ( copies == 0 );
+
+		CHECK ( inst1 != inst2 );
+
+		CHECK ( moves == 1 );
+		CHECK ( copies == 0 );
+
+		std::cout << inst1 << std::endl;
+
+		CHECK ( moves == 1 );
+		CHECK ( copies == 0 );
+
+		inst1.compare ( inst2 );
+
+		CHECK ( moves == 1 );
+		CHECK ( copies == 0 );
+	}
 }
-- 
GitLab