为什么需要带有 const 引用参数的重复代码

Why duplicate code is needed with const reference arguments?

本文关键字:参数 代码 引用 const 为什么      更新时间:2023-10-16

在这次采访中,Stepanov 展示了如何在C++中实现泛型max函数。

尝试以面向对象的方式实现一个简单的事情,比如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 &)。

为什么需要编写两次代码?这是否有助于编译器进行优化或约定以减少错误?max const版本的主体相同的特殊情况吗?

一个 N 个参数的函数应该有多少个有效的const和非const排列来定义一个完整的 API?

首先,您需要非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情况的实现。

因此,提供两次实现不是必需的,也不应该帮助编译器,但上述内容是否比 Stepanov 所做的更具可读性是值得商榷的。

您实际上不需要这两个版本。你可以这样写。

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.

如果您不打算修改参数,则可以使用const&版本。所有内容都应绑定到常量引用。

C++11 还引入了引用折叠,模板参数T&&有时称为通用引用。在这种情况下,当实例化例如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_);
}

这可以使用常量值、临时值(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);