【现代C++】简洁的as_cast函数模板

(点击上方公众号,可快速关注)

前言

C++17标准库加入了as_const函数模板,它可以将给定的左值引用转为常量左值引用,用法也很简单。下面的例子中,假设myValueMyType类型的值:

// C++17
const MyType& v = std::as_const(myValue);

实际上,它不是必需的,在C++17前已有多种方式实现这个功能,概括起来有三种,我们在下文一一介绍,并分析其优缺点,从侧面比较出std::as_const的简洁:

  • 方法一:使用语言内置的const_cast操作符

    const_cast<const T &>(myValue)
    

    这种方式能在老版本的C++98/03编译器中使用,早些年普遍使用,所以不用担心编译器的支持问题。

    但由于是操作符,缺乏模板的类型推导功能,所以在使用的时候必须提供尖括号内的类型,略显复杂。同时,const_cast是一个比较“危险”的转换操作,能不用就不用,很多C++书籍都提到了这一点。

  • 方法二:使用type trait

    const_cast<
    std::add_const_t<
      decltype(object)> &>(myValue)
    

    还有更复杂的:

    const_cast<
    std::add_const_t<
      std::remove_reference_t<
        decltype(myValue)>> &>(myValue)
    

    上面两种方式都使用type trait组合得到想要转换到的类型,本质上没有区别。

    繁冗拖沓估计是这种方式最大的缺点,没有好的编辑器,尖括号配对都是个难题;而且需要C++11以后的编译器支持,在C++98/03里是没法使用的;同样也使用了不讨喜的const_cast。在一定程度上,还不如方式一简洁。

  • 方法三:使用C语言的强制类型转换

    (const T &)myValue
    

    虽然这种方式不推荐在C++中用,但却是这三种方式中最简洁的。

as_const

通过上面各种方式的比较,我们需要的是一种比较简洁、不易出错的方式,合理的实现方式是模板,这样能通过类型推导而不需要指定尖括号内的类型,让使用更加简单,像一开始例子所展示的那样。我们看下它的声明:

template <class T>
constexpr std::add_const_t<T>& 
  as_const(T& t) noexcept;          (1)

template <class T>
void as_const(const T&&) = delete;  (2)

需要注意,该模板不能应用于右值引用类型,这是因为如果使用不当,可能会造成某个引用指向一个已经销毁的临时值。如下面的例子:

for(auto &val : 
    std::as_const(returns_container()))
{
    ...
}

returns_container()返回的临时值在as_const中返回后,实际上已经销毁了。val引用的值也是无效值,所以标准库禁用了该用法。

应用场景

典型应用在一些函数实现上,这类函数行为一致,但返回的类型不同,依调用的参数或对象类型而不同。由于只在其中一个函数维护主要逻辑,能避免代码冗余。比如,std::vectorbegin()cbegin()成员函数能根据参数类型的不同返回const和非const的迭代器;std::arrayoperator[]成员函数也能根据调用对象类型不同,返回const和非const的引用。

我们通常用用const版本的函数实现非const版本。下面以自定义数组的operator[]函数为例:

template <typename T>
class Array
{
private:
    T*     _elements;
    size_t _size;
public:
    T& operator[](size_t idx);
    const T& operator[](size_t idx) const;
    ...
};

template <typename T>
const T& Array<T>::operator[](size_t idx) const
{
    if (idx >= _size)
        throw std::out_of_range 
            {"Index too large: " + std::to_string(idx)};
    return _elements[idx];
}

template <typename T>
T& Array<T>::operator[](size_t idx)
{
      return const_cast<T&>(std::as_const(*this)[idx]);
}

参考资料

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0007r1.html

https://en.cppreference.com/w/cpp/utility/as_const

https://stackoverflow.com/questions/53450366/what-do-we-need-stdas-const-for

喜欢我的文章,请关注我的公众号。转载请标明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值