Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why duplicate code is needed with const reference arguments?

Tags:

c++

In this interview Stepanov shows how to implement generic max function in C++.

Try to implement a simple thing in the object oriented way, say, max. I do not know how it can be done. Using generic programming I can write:

template <class StrictWeakOrdered>
inline StrictWeakOrdered& max(StrictWeakOrdered& x,
StrictWeakOrdered& y) {
return x < y ? y : x;
}

and
template <class StrictWeakOrdered>
inline const StrictWeakOrdered& max(const StrictWeakOrdered& x,
const StrictWeakOrdered& y) {
return x < y ? y : x;
}

(you do need both & and const &).

Why is there need to write the code twice? Is this needed to aid compiler for optimization or a convention to reduce bugs? Is max a special case where body of a const version is identical?

How many valid const and non-const permutations a function of N arguments should have to define a complete API?

like image 254
sevo Avatar asked Jan 31 '16 21:01

sevo


2 Answers

First of all, you need the non-const version to allow stuff like

max(a, b) = something;

If you don't want to do such things, you can just provide the const version only to cover all cases. That is basically what the standard std::max does.

You also do not need to provide any more permutations of const and non-const, returning non-const& only makes sense if all inputs are non-const, all other cases are properly handled by the const version.

If you want to avoid code duplication, you can do something like this:

template <class StrictWeakOrdered>
inline StrictWeakOrdered& max(StrictWeakOrdered& x, StrictWeakOrdered& y) {
    const auto &xr = x;
    const auto &yr = y;
    return const_cast<StrictWeakOrdered&>(max(xr, yr));
}

In this special case, the const_cast is safe because you already know that the input is really non-const. Now you only have to provide the implementation for the const case.

So providing the implementation twice is not required and should not help the compiler, but whether or not the above is more readable than what Stepanov did is debatable.

like image 80
Baum mit Augen Avatar answered Nov 15 '22 20:11

Baum mit Augen


You actually don't need both versions. You can write it this way.

template <class S, class T>
decltype(auto) max(S&& a, T&& b) {
    using Ret = std::conditional_t<
          std::is_const<std::remove_reference_t<S>>::value, S, T>;

    if (b < a)
        return std::forward<Ret>(a);
    return std::forward<Ret>(b);
}

Falling back to const if either of the arguments was const.

like image 28
Yam Marcovic Avatar answered Nov 15 '22 21:11

Yam Marcovic