标签: C++17

  • C++17 std::variant 简单使用

    std::variant应当是用来替代union来使用的,后者从C时代就有了,但是它缺乏类型安全的保障。boost库很早之前就有了实现(其实从C++11开始,感觉boost不知道多少东西被借鉴走了)。
    std::variant的类长这个样子:

    template <class... Types>
    class variant;
    

    Usage

    使用起来也是比较方便的

    std::variant<int, double> v1;
    v1 = 1; // activate the "int" member
    assert(v1.index() == 0);
    assert(std::get<0>(v1) == 1);
    
    v1 = 3.14; // activate the "double" member
    assert(v1.index() == 1);
    assert(std::get<1>(v1) == 3.14);
    assert(std::get<double>(v1) == 3.14);
    
    assert(std::holds_alternative<int>(v1) == false);
    assert(std::holds_alternative<double>(v1) == true);
    
    assert(std::get_if<int>(&v1) == nullptr);
    assert(*std::get_if<double>(&v1) == 3.14);
    

    上面代码里的几个好用的constexpr函数:

    • std::get<>:可以传入index或是type。如果index或者类型不对的话,会跑出std::bad_variant_access异常;
    • std::get_if<>:这个是不抛异常的版本。注意参数和返回值类型都是指针——如果没有有效元素,那就会返回nullptr;
    • std::holds_alternative<Type>:判断一个Type在不在定义的variant参数类里面;

    Visiting variants

    如果要根据当前存储的类型来对variant对象做一些操作,第一会想到写一些if语句

    if (std::holds_alternative<int>(v1) == true) {
      // handle int value
    }
    else {
      // handle double value
    }
    

    但是如果variant的可选类型比较多的话,定义一个Visitor类会有助于按类型来执行操作,逻辑会显得清晰

    struct Visitor {
      double operator() (double d) { return d; }
      double operator() (int i) { return double(i); }
    };
    
    void show(std::variant<double, int> v) {
      std::cout >> std::visit(Visitor{}, v) << std::endl;
    }
    
    void test() {
      show(3.14);
      show(1);
    }
    
  • C++17 std::generate的使用

    std::generate是“遍历——执行”工具函数的一种

    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <vector>
    
    int main()
    {
      std::vector<std::string> v(4);
      std::generate(v.begin(), v.end(), [i=0]() mutable {
          return ++i % 2 ? "hello" : "world";
      });
      
      for (auto&& s : v) {
        std::cout << s << " ";
      }
      return 0;
    }
    

    上面的函数输出是hello world hello world .

    std::generate(beginIt, endIt, generator)函数接受起止的迭代器以及一个生成方法。

  • C++17 新特性—— if constexpr

    看代码说话

    template <typename Iterator>
    auto distance(Iterator begin, Iterator end) {
      using Traits = std::iterator_traits<Iterator>;
      if constexpr (std::is_base_of_v<std::random_access_iterator_tag, typename Traits::iterator_category>) {
        return end - begin;
      } else {
        auto result = typename Traits::difference_type();
        for (auto it = begin; it != end; ++it) {
          ++result;
        }
        return result;
      }
    }

    上面的代码用来判断两个iterator之间的距离。主要逻辑是判断iterator是否是可随机访问的iterator然后作分支处理,可以随机访问的迭代器直接把iterator相减。
    constexpr if语句中,条件的值必须是可按语境转换到 bool 类型的经转换常量表达式。若其值为 true,则舍弃 false分支语句(若存在),否则舍弃 true分支语句。有意思的一点是,被舍弃语句中的 return 语句不参与函数返回类型推导,也就是说两个分支语句中的return推倒出来的类型允许不一样。

    如果没有C++17这种怎么写呢?就是比较麻烦。
    一可以加一个参数,但是每个tag写一个

    template <typename Iterator>
    auto distance(Iterator begin, Iterator end, std::bidirectional_iterator_tag); // one for each tag type

    或者用SFINAE写两个

    template <typename Iterator>
    auto distance(Iterator begin, Iterator end, 
                  typename std::enable_if_t<std::is_base_of_v<一大坨>>* = nullptr);