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

news/2024/7/24 4:40:25 标签: 编程语言, java, 指针, scala, c++11

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

前言

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

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


http://www.niftyadmin.cn/n/1027754.html

相关文章

C++核心准则T.61:不要过度参数化成员(SCARY)

T.61: Do not over-parameterize members (SCARY) T.61:不要过度参数化成员&#xff08;SCARY&#xff09; Reason&#xff08;原因&#xff09; A member that does not depend on a template parameter cannot be used except for a specific template argument. This limi…

【现代C++】新的字符串格式化方法

&#xff08;点击上方公众号&#xff0c;可快速关注&#xff09;本篇文章主要介绍现代C字符串格式化的方法。在此之前&#xff0c;回顾了一些老的字符串格式化的方法&#xff0c;并分析各自的优劣。在最后给出了一种提供给老编译器的折中方案&#xff0c;因为新的格式化方法需要…

C++核心准则T.62:将非依赖类模板成员放入非模板基类中

T.62: Place non-dependent class template members in a non-templated base class T.62&#xff1a;将非依赖类模板成员放入非模板基类中 Reason&#xff08;原因&#xff09; Allow the base class members to be used without specifying template arguments and without…

【C++】std::endl只是换行?

&#xff08;点击上方公众号&#xff0c;可快速关注&#xff09;前言一直用std::endl输出换行符&#xff0c;理所当然认为下面的代码等价&#xff1a;std::cout << "Hello Wolrd" << std::endl; std::cout << "Hello Wolrd" << \n…

【C++】再谈 STL reserve的坑

&#xff08;点击上方公众号&#xff0c;可快速关注&#xff09;前言之前写过一篇文章《STL reserve函数使用误区》&#xff0c;主要内容是说明一些标准模板类&#xff0c;比如&#xff0c;std::vector、std::string等&#xff0c;提供的reserve成员函数改变的是capacity&#…

C++核心准则T.64​:使用特化提供类模板的不同实现

T.64: Use specialization to provide alternative implementations of class templates T.64&#xff1a;使用特化提供类模板的不同实现 Reason&#xff08;原因&#xff09; A template defines a general interface. Specialization offers a powerful mechanism for prov…

【现代C++】自定义字面量-序:字面量(一)

&#xff08;点击上方公众号&#xff0c;可快速关注&#xff09;C11引入了自定义字面量&#xff0c;由于这个语言特性的外延较大&#xff0c;为了系统介绍它&#xff0c;先把字面量的知识补全。在看语言规范的过程中&#xff0c;的确发现了好多知识点是以前没接触过的&#xff…

【C++】巨坑-VC++的localtime_s的三宗罪

&#xff08;点击上方公众号&#xff0c;可快速关注&#xff09;前几天在运行一段代码的时候&#xff0c;发现localtime执行失败&#xff0c;由于代码没有判断返回值&#xff0c;后续对空指针操作导致段错误。所以&#xff0c;需要对该段代码增加保护判断&#xff0c;避免程序崩…