zoukankan      html  css  js  c++  java
  • c++ 17介绍

    作者:hearts zh
    链接:https://www.zhihu.com/question/32222337/answer/55238928
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    其实现在的proposal很多很多,不出意外也会有相当一部分没时间加进c++17。c++17会是一个大更新,下一个小修补会是c++20

    我个人认为从发展角度来说,c++目前最需要的,是module,网络库,以及更完善的并行库。我只稍微搬一下Stroustrup桑今年最新提交的提案,提到了他认为的c++17是需要包括哪些东西。

    全文具体链接。


    首先,C++永久性的目的就是运行效率。无论怎么设计都不会违背这一点。
    Whatever we do, we should preserve C++’s fundamental strengths:
    • A direct map to hardware (initially from C)
    • Zero-overhead abstraction (initially from Simula)
    Depart from these and the language is no longer C++.

    1. 关于大规模软件系统的支持

      • Modules:不出意外,这个呼声太高了。不但是编译速度,就连最基础的One Definition Rule的各种规则都能搞死人。最新的2篇modules提案:
        说起来简单,实现起来麻烦。目前提案者正在Visual C++中实现这些提案。因为这个呼声很高,有理由相信进入c++17的可能性很大。虽然目前进展还不是很快的感觉。看proposal目前还有一些小的design decision没有明确下来,例如是否需要interface这个关键字之类的。

        有了modules写出来的代码是什么样子呢,大概是这个样子,

        import std.vector;
        import std.string;
        import std.iostream;
        import std.iterator;
        

        怎样写module呢?

        //abc.cpp (文件名无需和module名重合)
        module abc;
        struct internal {  };
        int internal_f() {  }
        export namespace abc 
        {
          class abc { };
        }
        

        注意C++的module仅仅只是编译范畴的,并不是二进制范畴的。效率为王。

    • Contracts:经过11年被洗礼之后,这次加进来问题应该不大了。最新的提案:



      出来时间比较久了,大家也都知道是干嘛的,也有刘雨培童鞋说过最新提案,我就不多说了,其实Contracts简化后也是蛮复杂的,但基本上即使你从没接触过,读懂别人写的代码的难度也不大。
    • A type-safe union:也就是variant,基本上是在往functional-programming上靠。

      Stroustrup桑还给了一个链接,13年他的一个学生(?)的论文,一个c++的pattern matching库,蛮坑的。我觉得不进行大改善,这个pattern matching库进入c++的标准可能性不大。

      好奇的童鞋们不用找了,我拷个最简单的例子出来。有了variant之后,这个match库可以match类型+值。不过仅仅是库而不是语言层面的支持,真是不伦不类啊。

      int factorial(int n) {
        int m;
        Match(n) {
          Case (1) return 0;
          Case (m) return m*factorial(m-1);
          Case (_) throw std::invalid_argument(""); 
        } EndMatch
      }
      

    2. 关于并行
      • 基础网络库:这个太需要了,没有基础网络库,其他任何网络相关的操作都是空中楼阁。

        基于boost::asio的最新提案,



        boost::asio的async模型基本是callback。但这个proposal其实并不是简单的boost::asio直接拷贝过来,除了基本的asio之外,还基于future和co-routine加了不少料。

        例如,基于future的async模型,

        std::future<std::size_t> fut =
          socket.async_read_some(buffer(buffer_space), use_future);
        
        // ...
        
        std::size_t length = fut.get();
        
        复杂一点还可以这样,

        socket.async_read_some(buffer(buffer_space), use_future).then([](std::size_t) {});
        

        例如,基于co-routine的async模型(例子是stackful的,stackless需要语言层面支持)。

        void coro_connection(tcp::socket& socket, yield_context yield)
        {
          try
          {
            std::vector<char> buffer_space(1024);
            for (;;)
            {
              std::size_t length = socket.async_read_some(buffer(buffer_space), yield);
              uppercase(buffer_space.begin(), buffer_space.begin() + length);
              async_write(socket, buffer(buffer_space, length), yield);
            }
          }
          catch (std::system_error& e)
          {
            // ...
          }
        }
        
        基本上可以看出网络库是一个async模型的大杂烩,只要你有,我就支持。

        最后,这个proposal还加了一些高级的库(无需关注底层细节)。

        tcp::iostream s("www.boost.org", "http");
        
        s << "GET / HTTP/1.0
        ";
        s << "Host: www.boost.org
        ";
        s << "Accept: */*
        ";
        s << "Connection: close
        
        ";
        
        std::string header;
        while (std::getline(s, header) && header != "
        ")
          std::cout << header << "
        ";
        std::cout << s.rdbuf();
        

    • SIMD vector:数学计算啊,游戏啊,没什么好说的,研究这方面的自然懂,不研究的这个概念也没啥复杂的。下面这个提案提出一个matrix 类,乘法等操作利用simd搞计算。

    • Improved Future: 基本上都是微软根据.Net经验提出来的。就比如网络库的.then,就是基于此。你可以async_read( ).then().then().then(),等等。具体看proposal吧,蛮简单易懂的。

    • Co-routines:主体也是微软搞出来的提案,基本和.net一样的语法。在VC++上已经有实现。本来这东西就分stackful还是stackless。微软应该是比较倾向于stackless的,google一人也提出了stackful和stackless的语法可以统一的一个提案。具体还未定。很可能是都支持,用户可自由选择的。其实微软的提案还是比较吸引人。关键字:await,yield



      我要多说一些,因为蛮有意思,

      复制一下微软的design goal:

      * Highly scalable (to billions of concurrent coroutines)
      * Highly efficient resume and suspend operations comparable in cost to a function call overhead
      * Seamless interaction with existing facilities with no overhead
      * Open ended coroutine machinery allowing library designers to develop coroutine libraries exposing various high-level semantics, such as generators, goroutines, tasks and more
      * Usable in environments where exception are forbidden or not available

      就看他的design goal就很想敢用了

      写出来的代码什么样子呢?await和yield关键字其实.net, python啊,java啊之类的也都有,没什么好解释的,要说下generator和goroutine,你没看错,是类Go语言的goroutine支持。

      generator:
      generator<int> fib(int n) {
        int a = 0;
        int b = 1;
        while (n-- > 0) {
          yield a;
          auto next = a + b;
          a = b;
          b = next;
        }
      }
      
      goroutine:
      goroutine pusher(channel<int>& left, channel<int>& right)
      {
        for(;;) {
          auto val = await left.pull();
          await right.push(val + 1);
        }
      }
      int main() {
        static const int N = 1000 * 1000;
        std::vector<channel<int>> c(N + 1);
        for(int i = 0; i < N; ++i)
          goroutine::go(pusher(c[i], c[i + 1]));
        c.front().sync_push(0);
        std::cout << c.back().sync_pull() << std::endl;
      }
      


    • Trasactional Memory: 底部支持,没什么好说的。基本是板上钉钉。
    • Parallel STL

      基本上就是并行实现的stl,可以选择模式,例如sort

      sort(begin(v), end(v)); //无并行
      sort(seq, begin(v), end(v)); //无并行
      sort(pal, begin(v), end(v)); //并行
      sort(par_vec, begin(v), end(v)); //并行矢量化
      execution_policy exec=seq; //动态决定
      if (v.size() > 1000) exec = par;
      sort(exec, begin(v), end(v));
      

    3. 语言用法简化
    • Concepts: 不说了
    • Ranges: 也有童鞋说过了,不说了。总体来说ranges就是一对数[i, j),或者一对iterator。需要对STL库进行添加支持针对Ranges的操作。例如你可以sort(v),而无需sort(begin(v), end(v))
    • Default comparisons:就是说会自动生成operator >, ==之类的。对用户自定义move或者copy的不会自动生成。
    • Uniform call syntex:也有童鞋说过了。

      目前这个提案大概是确认了:如果f(x,y),那么就先找函数,如果函数没有,再找x.f(y)。而x.f(y)相反,找不到类x的f函数,再去找 f(x,y)函数。也有另外一个选择,就是x.f(y)和f(x,y)所有符合的全部放到一起然后做overloading resolution。这样可能会破坏现有的代码。还有2年时间可以讨论选哪种。

    • Operator dot: 没什么可说的
    • array_view和string_view: 蛮有意思的东西。

      假设你有一个字符串“abcde, xyz",目前来说,如果你实现一个函数,从逗号分割这个字符串,你会得到2个字符串"abcd", "xyz"。但如果有了string_view,你会得到2个string_view,每个string_view里面是一个(start, size)对,这样就节省了多个字符串拷贝。

      array_view可以更有意思,
      auto M = 32;
      auto N = 64;
      auto v = vector<float>(M * N);
      auto av = array_view<float, 2>({M, N}, v);
      
      你要问为啥不直接 vector<vector<float>>,因为这样无法保证内存连续分配啊。

    • stack_array:还没有proposal
    • optional,和variant类似吧。目前没有proposal,除非pattern matching被加到c++17中去了,否则不太可能17里实现。

    以上就是stroustrup桑眼里的c++17应该大概支持这些。当然不是一个完整列表,不包括一些库的改进和语法小改进。
     
     
     
    不要想啦,C++ 17 毛都没有。。。没有 Module, 没有 Coroutine, 没有 Ranges,没有 Reflection,没有 Contracts,没有……
    唯一有希望的 Concepts 争议很大。。也玄。
    Network TS 也玄。
    所以,……

    --------------------------------------------------------------------------------------------------------------------------------
    我的天,看到这个问题我激动得不知道说些什么好。在 BS 的那篇 Thoughts about C++ 17 里面已经提到了很多提案了。我说一点我研究过的吧,算是抛砖引玉:
    先说一些开胃菜吧。
    • 模板的模板参数允许使用 typename(之前仅允许使用 class)
    • 修改了 initializer_list 的推导原则:
    For copy-list-initialization, auto deduction will either deduce a std::initializer_list (if the types of entries in the braced-init-list are all identical) or be ill-formed otherwise.

    For direct list-initialization:
    1.For a braced-init-list with only a single element, auto deduction will deduce from that entry;
    2.For a braced-init-list with more than one element, auto deduction will be ill-formed.
    unique_ptr<Foo const * const []> ptr1(new Foo*[10]); 
    Foo const * ptr = ptr1[9];  
    
    • 嵌套的 namespace:
    namespace A::B::C {
        //…
    } 
    
    相当于:
    namespace A {
        namespace B {
            namespace C {
                //…
            }
        }
    } 
    

    • Fold Expressions:
    template<typename... T>
    
    auto sum(T... s){
    
        return (... + s);
    
    } 
    
    再说说大的:
    • Concepts
    在CppCon 2014里面BS发表了主题为 Make Simple Things Simple 的演讲,里面提到了Concepts。根据他的意思,我们以后可以这么写代码:
    注意!Sortable可不是Java/C#里的Interface,它叫做Concept。
    • Module
    对于Module也可以说是呼声极高了,可以大幅加快编译速度,带来相当多的好处,不过我对于Module的提案没有太多研究。
    • Contracts
    Contracts 目前的提案是N4415。大概意思就是为每个函数指定一个 pre-conditions 与 post-conditions。大概像是这样:
    T& operator[](size_t i) [[expects: i < size()]]; 
    
    ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];   
    
    那么不满足这些conditions会发生什么呢?提案中说是“implemention-defined”。而且应该允许每个TU独立打开所有的测试,关闭所有,打开/关闭pre,打开关闭post。
    • Unified Call Syntax
    这个真的是太优美了。HS和BS分别提了一篇提案,后来俩人又一起弄了一篇提案。这个说的是什么呢,就是统一f(x,y)与x.f(y),这样就不用std::begin一个,container.begin()又一个了。HS的提案中提到了这样的应用场景:
    void f(FILE* file)
    {
       fseek(file,9,SEEK_SET);
    }
    
    //proposed new C++ code
    void f(FILE* file)
    {
        file->fseek(9,SEEK_SET); //nice autocomplete after "->"
    }
    
    不过现在这个提案还有很多细节在商讨。
    说了一些我了解的Core Language特性,说一些库的东西吧!先说点小东西
    • std::invoke
    让我们看看如何实现一个apply:
    template <class F, class Tuple, std::size_t... I>
    constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
    {
    	return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
    	// Note: std::invoke is a C++17 feature
    }
    template <class F, class Tuple>
    constexpr decltype(auto) apply(F&& f, Tuple&& t)
    {
    	return apply_impl(std::forward<F>(f), std::forward<Tuple>(t),
    		std::make_index_sequence < std::tuple_size<std::decay_t<Tuple>>::value > {});
    } 
    

    INVOKE的概念一直在标准里面,这回终于有了真正的invoke了。

    • void_t
    有了Expression SFINAE和void_t,写模板真的是方便了太多了。就像这样:
    template<typename Iterator,
    			typename = void>
    	struct reference_type
    	{
    		using type = decltype(*declval<Iterator>()); // no reference, use operator*
    	};
    
    	template<typename Iterator>
    	struct reference_type<Iterator,
    		void_t<typename Iterator::reference>
    	>
    	{
    		using type = typename Iterator::reference; //I have a reference
    	}; 
    

    再说说大的。目前这些都属于 TS 。
    • 利用variable templates:
    namespace std {
    namespace experimental {
    inline namespace fundamentals_v2 {
    
      // See C++14 §20.10.4.1, primary type categories
      template <class T> constexpr bool is_void_v
        = is_void<T>::value;
      template <class T> constexpr bool is_null_pointer_v
        = is_null_pointer<T>::value;
    //.... 
    
    • filesystem
    • network
    • Ranges

    Ranges!这个我必须说一说。我们经常写

    std::sort(std::begin(v),std::end(v),std::greater<>{});
    

    那个begin end太烦了。Ranges就是为了解决这个问题:

    std::sort(v,std::greater<>{});
    

    当然远远不止这点,Ranges 里面的东西还可以花样组合。你还可以写出这样的东西:

    int total = accumulate(view::iota(1) |
                           view::transform([](int x){return x*x;}) |
                           view::take(10), 0);
    
    其实背后的概念还是挺多的,例如 Iterable 等等。详细的可以去看Ranges for the Standard Library, Revision 1
    • Type-erased Allocator

    这个真的是深得我心啊!我最近正在按照目前的TS Draft实现这个东西。就是说一个vector:

    std::vector<int,std::allocator<int>> v1;
    std::vector<int,MyAllocator<int>> v2;
    v1 = v2;//Error
    

    由于Allocator属于类型的一部分,导致不同Allocator的vector不能copy啊等等。而且个人认为std::allocator有点鸡肋。这回好了,有了一个叫做memory_resource的抽象类:

    class memory_resource {
      // For exposition only
      static constexpr size_t max_align = alignof(max_align_t);
    
    public:
      virtual ~memory_resource();
    
      void* allocate(size_t bytes, size_t alignment = max_align);
      void deallocate(void* p, size_t bytes,
                      size_t alignment = max_align);
    
      bool is_equal(const memory_resource& other) const noexcept;
    
    protected:
      virtual void* do_allocate(size_t bytes, size_t alignment) = 0;
      virtual void do_deallocate(void* p, size_t bytes,
                                 size_t alignment) = 0;
    
      virtual bool do_is_equal(const memory_resource& other) const noexcept = 0;
    };
    

    之后有五种内置的多态allocator:

    • new_delete_resource(),使用::operator new/delete
    • null_memory_resource(),使用allocate就会抛出std::bad_alloc
    • synchronized_pool_resource
    • unsynchronized_pool_resource
    • monotonic_buffer_resource

    有一个pmr::polymorphic_allocator的类满足Allocator requirements,将一个memory_resource包装起来:

    #include <deque>
    
    namespace std {
    namespace experimental {
    inline namespace fundamentals_v2 {
    namespace pmr {
    
      template <class T>
      using deque = std::deque<T,polymorphic_allocator<T>>;
    
    } // namespace pmr
    } // namespace fundamentals_v2
    } // namespace experimental
    } // namespace std
    
    #include <forward_list>
    
    namespace std {
    namespace experimental {
    inline namespace fundamentals_v2 {
    namespace pmr {
    
      template <class T>
      using forward_list =
        std::forward_list<T,polymorphic_allocator<T>>;
    
    } // namespace pmr
    } // namespace fundamentals_v2
    } // namespace experimental
    } // namespace std
    

    当然,我只是说了我了解的。还有很多其他的并发、并行算法、SIMD vector、string_view/array_view、optional/variant/any我没有做深入了解,就不误导大家了。

  • 相关阅读:
    git的使用
    模块化的 require 和 import的区别
    Javascript模块化编程(二)commonJS规范和AMD规范
    Javascript模块化编程(三):require.js的用法
    SQL精华语句
    Convert sql 函数格式
    sql分页存储过程(汇总)
    jira和svn结合
    使用Java Service Wrapper 把Java程序作为Windows系统服务
    Eclipse使用技巧
  • 原文地址:https://www.cnblogs.com/timssd/p/5544693.html
Copyright © 2011-2022 走看看