(点击上方公众号,可快速关注)
前言
C++17标准库加入了as_const
函数模板,它可以将给定的左值引用转为常量左值引用,用法也很简单。下面的例子中,假设myValue
是MyType
类型的值:
// 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::vector
的begin()
和cbegin()
成员函数能根据参数类型的不同返回const
和非const
的迭代器;std::array
的operator[]
成员函数也能根据调用对象类型不同,返回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
喜欢我的文章,请关注我的公众号。转载请标明出处。