Подтвердить что ты не робот

Зачем нужен дублирующий код с аргументами const?

В этом интервью Степанов показывает, как реализовать универсальную функцию max в С++.

Попробуйте реализовать простую вещь объектно-ориентированным способом, скажем, макс. Я не знаю, как это можно сделать. Используя общее программирование, я могу написать:

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;
}

(вам нужны оба и и const и amp;).

Зачем нужно писать код дважды? Нужно ли это помогать компилятору для оптимизации или конвенции для сокращения ошибок? Является ли max особым случаем, когда тело версии const идентично?

Сколько допустимых перестановок const и не const функция из N аргументов должна определять полный API?

4b9b3361

Ответ 1

Прежде всего, вам нужна версия const, чтобы разрешить такие вещи, как

max(a, b) = something;

Если вы не хотите делать такие вещи, вы можете просто предоставить версию const только для всех случаев. Это в основном то, что делает стандартный std::max.

Вам также не нужно предоставлять больше перестановок const и не const, возврат не const& имеет смысл только в том случае, если все входы не являются const, все остальные случаи должным образом обрабатываются версии const.

Если вы хотите избежать дублирования кода, вы можете сделать что-то вроде этого:

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));
}

В этом специальном случае const_cast безопасен, поскольку вы уже знаете, что вход действительно не const. Теперь вам нужно предоставить реализацию для случая const.

Таким образом, реализация реализации дважды не требуется и не должна помогать компилятору, но независимо от того, читается ли выше, чем то, что сделал Степанов, является спорным.

Ответ 2

На самом деле вам не нужны обе версии. Вы можете написать это так.

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);
}

Возвращаясь к const, если любой из аргументов был const.

Ответ 3

Если вы не собираетесь изменять аргумент, вы можете просто перейти с версией const&. Все должно связываться с константной ссылкой.

В С++ 11 также введено обращение коллапса, а параметр шаблона T&& иногда называют универсальной ссылкой . В этом случае при создании типа параметра, например. a int&, мы имели бы int& &&, который сворачивается в int&. Теперь вы можете записать функцию как

template <class T1, class T2>
inline T1 const& max(T1&& x, T2&& y)  {
     T1 const& x_=x;
     T2 const& y_=y;
     return (x_ < y_) ? (y_) : (x_);
}

Это можно вызвать со значениями const, временными (r-значениями) и изменяемыми переменными:

int const a=1;
int b=2;
max(b,b) = 23;
std::cout << max(a,a) << max( int{4}, int{5} ) << b << max(int{4}, a);