Avoiding code duplication between syntactically identical const and non const functions that are not semantically identical

#include <iostream> using namespace std; class A { public: A() : x(0) {} // notice: not identical to const version but does update void FA() {std::cout << "A" << std::endl; x++;} void FA() const {std::cout << "const A" << std::endl;} private: int x; }; class B { public: B() : x(0) {} // notice: not identical to const version but does update void FB() {std::cout << "B" << std::endl; x++;} void FB() const {std::cout << "const B" << std::endl;} private: int x; }; class C { public: void FC() { bool condition = true; // should be set to a real condition if(condition) { a.FA(); } b.FB(); } void FC() const { bool condition = true; // should be set to a real condition if(condition) { a.FA(); } b.FB(); } private: A a; B b; }; int main() { C c; c.FC(); const C cc; cc.FC(); return 0; } 

First, sorry for the long headline. How to avoid code duplication in class C in FC, FC const functions? given that you cannot use the trick of casting this to const and invoking the const FC version from the non const FC version, because the body of non const FC will actually call functions that will perform updates that are not identical to their respective constants.

+5
source share
2 answers

Let the template member function do the actual work. In other words: Try the following:

 class C { public: void FC() { FC_Impl( *this ); } void FC() const { FC_Impl( *this ); } private: template <typename Self> static void FC_Impl( Self & self ) { bool condition = true; // should be set to a real condition if(condition) { self.a.FA(); } self.b.FB(); } A a; B b; }; 
+7
source

Ralph's excellent answer. I think it's worth mentioning that providing free functions with the same name but with different types of arguments, we allow ourselves to express our intention at a high level, while free functions act like adapters. We allow ADL to find the right overload (or specialization) function for us.

Thus, we do not need to know the names of member functions in order to express logic. In this example, this will make the C class more suitable for modification and evolution later:

 #include <iostream> #include <utility> using namespace std; // general case template<class T> decltype(auto) apply_F(T&& t) { return tF(); } // call apply_F with either an X or a Y and return the result )can be void) template<class X, class Y> auto conditionally_apply_F(bool condition, X&& x, Y&& y) -> std::common_type_t<decltype(apply_F(x)), decltype(apply_F(y))> { if (condition) { return apply_F(x); } else { return apply_F(y); } } // provides F member function - no need for specific adapter class A { public: A() : x(0) {} // notice: not identical to const version but does update void F() {std::cout << "A" << std::endl; x++;} void F() const {std::cout << "const A" << std::endl;} private: int x; }; // has differing names implementing the concept of F - so we'll need an adapter class B { public: B() : x(0) {} // notice: not identical to const version but does update void F_mutable() {std::cout << "B" << std::endl; x++;} void F_const() const {std::cout << "const B" << std::endl;} private: int x; }; // adapter overloads decltype(auto) apply_F(B const& t) { return t.F_const(); } decltype(auto) apply_F(B& t) { return t.F_mutable(); } // class C current expressed in terms of A and B, but no longer tied to these types class C { public: void FC() { bool condition = true; // should be set to a real condition conditionally_apply_F(condition, a, b); } void FC() const { bool condition = true; // should be set to a real condition conditionally_apply_F(condition, a, b); } private: A a; B b; }; int main() { C c; c.FC(); const C cc; cc.FC(); return 0; } 
0
source

Source: https://habr.com/ru/post/1262744/


All Articles