How to Remove Code Duplication Between Similar Const and Non-Const Member Functions

How do I remove code duplication between similar const and non-const member functions?

Yes, it is possible to avoid the code duplication. You need to use the const member function to have the logic and have the non-const member function call the const member function and re-cast the return value to a non-const reference (or pointer if the functions returns a pointer):

class X
{
std::vector<Z> vecZ;

public:
const Z& z(size_t index) const
{
// same really-really-really long access
// and checking code as in OP
// ...
return vecZ[index];
}

Z& z(size_t index)
{
// One line. One ugly, ugly line - but just one line!
return const_cast<Z&>( static_cast<const X&>(*this).z(index) );
}

#if 0 // A slightly less-ugly version
Z& Z(size_t index)
{
// Two lines -- one cast. This is slightly less ugly but takes an extra line.
const X& constMe = *this;
return const_cast<Z&>( constMe.z(index) );
}
#endif
};

NOTE: It is important that you do NOT put the logic in the non-const function and have the const-function call the non-const function -- it may result in undefined behavior. The reason is that a constant class instance gets cast as a non-constant instance. The non-const member function may accidentally modify the class, which the C++ standard states will result in undefined behavior.

Remove duplication in equivalent const and non-const members when returning optional reference by value

You might do something similar to:

class Foo {
boost::optional<const Bar&> get_bar() const;
boost::optional<Bar&> get_bar()
{
auto opt = static_cast<const Foo&>(*this).get_bar();
if (opt) return const_cast<Bar&>(*opt);
return boost::none;
}
};

How do I remove code duplication between similar const and non-const member functions in abstract classes?

Yes, if you want a user to call a method you provide with either a const or a non-const this pointer, then you must provide at least a const version of the function to call. Note that you can call a const method with a non-const this pointer.

Consider if Execute needs to be non-const at all. There is at least a non-zero chance that if you are able to provide a const version of Execute, then the nonconst version of Execute is totally unnecessary.

So, in direct response to your question:

How do I remove code duplication between similar const and non-const
member functions in abstract classes?

Maybe by eliminating the non-const member function altogether.

how to avoid code duplication with const and non-const member functions being input into templates

Unfortunately, the presence or absence of const on a non-static member function is not a feature that can be deduced separately from the function type it appertains to. Therefore, if you want to write a single foo template declaration that is limited to accepting pointers to members (but accepts both const and non-const member functions) then it would have to be:

template <class MemberOf, class F>
void foo(F MemberOf::*func);

For example:

#include <type_traits>

template <class MemberOf, class F>
void foo(F MemberOf::*func) {
static_assert(std::is_same<F, void(int) const>::value);
}

struct S {
void bar(int) const {}
};

int main() {
foo(&S::bar);
}

You cannot have F's argument types deduced at that point. You would have to dispatch to a helper function. (But we cannot deduce all the types at once while also writing a single declaration that accepts both const and non-const. If that's the only thing you'll accept, then sorry, it's not possible.) We can do this like so:

template <class T>
struct remove_mf_const;

template <class R, class... Args>
struct remove_mf_const<R(Args...)> {
using type = R(Args...);
};

template <class R, class... Args>
struct remove_mf_const<R(Args...) const> {
using type = R(Args...);
};

template <bool is_const, class F, class OutType, class MemberOf, class... ArgTypes>
void foo_helper(F func, OutType (MemberOf::*)(ArgTypes...)) {
// now you have all the types you need
}

template <class MemberOf, class F>
void foo(F MemberOf::*func) {
static_assert(std::is_function<F>::value, "func must be a pointer to member function");
using NonConstF = typename remove_mf_const<F>::type;
constexpr bool is_const = !std::is_same<F, NonConstF>::value;
foo_helper<is_const>(func, (NonConstF MemberOf::*)nullptr);
}

Coliru link

Removing code duplication from const and non-const methods that return iterators

I believe, it is only possible with the helper

typedef int Z;

class X
{
std::vector<Z> vecZ;
public:
std::vector<Z>::iterator foo(size_t index)
{
return helper(*this);
}
std::vector<Z>::const_iterator foo(size_t index) const
{
return helper(*this);
}

template <typename T>
static auto helper(T& t) -> decltype(t.vecZ.begin())
{
return t.vecZ.begin();
}
};

EDIT
Same can be implemented without c++11

template <typename T>
struct select
{
typedef std::vector<Z>::iterator type;
};
template <typename T>
struct select<const T&>
{
typedef std::vector<Z>::const_iterator type;
};

template <typename T>
static
typename select<T>::type
helper(T t)
{
return t.vecZ.begin();
}

But, well, I think you should think twice before using this approcach

How to avoid code duplication between similar const and non-const member functions which pass class members to callbacks?

You can use a static member function template to forward *this to a generic function parameter:

template<typename Self, typename F>
static void for_each_hurg(Self& s, F&& f) {
for (auto& h : s.hurgs) {
f(h);
}
}

template<typename F>
void for_each_hurg(F&& f) { for_each_hurg(*this, forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) const { for_each_hurg(*this, forward<F>(f))); }

Since the advent of reference qualifiers for member functions, the general solution is to perfectly forward *this. This doesn't always matter, since you often don't want member functions to be called on rvalues anyway. I'll add this here since I think it is part of a more general solution.

Unfortunately, *this is always an lvalue, so you need additional manual care in the member function wrappers:

template<typename Self, typename F>
static void for_each_hurg(Self&& s, F&& f) {
/* ... */
}

template<typename F>
void for_each_hurg(F&& f) && { for_each_hurg(move(*this), forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) & { for_each_hurg(*this, forward<F>(f))); }

Which is unfortunately not symmetric :(


It is also possible to implement the above via a friend function template. This can have two benefits:

  • You can move the friend function template to namespace scope, which in the case of furg being a class template reduces the amount of function templates the compiler has to deal with (one per class template, instead of one per instantiation). This typically requires some boilerplate code and forward-declarations, though.
  • You can call the function with the same name either as a free function or as a member function, e.g. furg f; for_each_hurg(furg, [](hurg){}); furg.for_each_hurg([](hurg){}); (When unqualified lookup finds a member function, it doesn't perform / ignores the results of ADL. Therefore, you'd have to put the friend function in namespace scope, in order to be able to refer to it via a qualified-id from within the non-static member function wrappers.)

Additionally, you'd have to protect that function template from being to greedy; either by putting it into some namespace detail or adding an enable-if clause. It's probably not worth the effort.

avoiding code duplication in const and non-const member functions [duplicate]

Scott Meyers solved a similar problem in Item 3 of Effective C++ by calling the const version inside the non-const version and then casting the const result back to non-const:

const_iterator find(const T& value) const
{
// actual implementation of find
}

iterator find(const T& value)
{
return const_cast<iterator>(static_cast<const container*>(this)->find(value));
}

How to handle const/non const combination of getters without duplicate code? [duplicate]

I hate to say it, but const_cast. What you do is make and call the const getter, and just remove the const that it returns since you know you are in a non-const object inside the non const getter. That would look like

  const Season* GetSeason(const std::string& name) const
{
const int index = findIndex(_seasons, [name](const Season& s) { return s.seasonName == name; });
return index != -1 ? &_seasons[index] : nullptr;
}
Season* GetSeason(const std::string& name)
{
return const_cast<Season*>(const_cast<SeasonCollection const *>(this)->GetSeason(name));
// ^ remove the const^ ^ add const to call the const getter ^^call const getter^
}

Avoid literally duplicating code for const and non-const with auto keyword?

Ok, so after a bit of tinkering I came up with the following two solutions that allow you to keep the auto return type and only implement the getter once. It uses the opposite cast of what Meyer's does.

C++ 11/14

This version simply returns both versions in the implemented function, either with cbegin() or if you don't have that for your type this should work as a replacement for cbegin(): return static_cast<const A&>(*this).examples.begin(); Basically cast to constant and use the normal begin() function to obtain the constant one.

// Return both, and grab the required one
struct A
{
private:
// This function does the actual getter work, hiding the template details
// from the public interface, and allowing the use of auto as a return type
auto get_do_work()
{
// Your getter logic etc.
// ...
// ...

// Return both versions, but have the other functions extract the required one
return std::make_pair(examples.begin(), examples.cbegin());
}

public:
std::vector<int> examples{ 0, 1, 2, 3, 4, 5 };

// You'll get a regular iterator from the .first
auto get()
{
return get_do_work().first;
}

// This will get a const iterator
auto get() const
{
// Force using the non-const to get a const version here
// Basically the inverse of Meyer's casting. Then just get
// the const version of the type and return it
return const_cast<A&>(*this).get_do_work().second;
}

};

C++ 17 - Alternative with if constexpr

This one should be better since it only returns one value and it is known at compile time which value is obtained, so auto will know what to do. Otherwise the get() functions work mostly the same.

// With if constexpr
struct A
{
private:
// This function does the actual getter work, hiding the template details
// from the public interface, and allowing the use of auto as a return type
template<bool asConst>
auto get_do_work()
{
// Your getter logic etc.
// ...
// ...

if constexpr (asConst)
{
return examples.cbegin();

// Alternatively
// return static_cast<const A&>(*this).examples.begin();
}
else
{
return examples.begin();
}
}

public:
std::vector<int> examples{ 0, 1, 2, 3, 4, 5 };

// Nothing special here, you'll get a regular iterator
auto get()
{
return get_do_work<false>();
}

// This will get a const iterator
auto get() const
{
// Force using the non-const to get a const version here
// Basically the inverse of Meyer's casting, except you get a
// const_iterator as a result, so no logical difference to users
return const_cast<A&>(*this).get_do_work<true>();
}
};

This may or may not work for your custom types, but it worked for me, and it solves the need for code duplication, although it uses a helper function. But in turn the actual getters become one-liners, so that should be reasonable.

The following main function was used to test both solutions, and worked as expected:

int main()
{
const A a;
*a.get() += 1; // This is an error since it returns const_iterator

A b;
*b.get() += 1; // This works fine

std::cout << *a.get() << "\n";
std::cout << *b.get() << "\n";

return 0;
}

Elegant solution to duplicate, const and non-const, getters? [duplicate]

I recall from one of the Effective C++ books that the way to do it is to implement the non-const version by casting away the const from the other function.

It's not particularly pretty, but it is safe. Since the member function calling it is non-const, the object itself is non-const, and casting away the const is allowed.

class Foo
{
public:
const int& get() const
{
//non-trivial work
return foo;
}

int& get()
{
return const_cast<int&>(const_cast<const Foo*>(this)->get());
}
};


Related Topics



Leave a reply



Submit