Rust 1.38 意气风发度发表了,Rust 1.38.0 稳固版内容如下:

Rust 1.36.0 稳定版发表了。

C++11模板元编制程序,11模板编制程序

管道编写翻译(pipelined compilation卡塔尔国

要是以前曾经经过 rustup 安装了 Rust,那么能够因而以下方法获得 Rust
1.36.0:

1.概述

  模版元编制程序(template
metaprogram)是C++中最复杂也是威力最苍劲的编制程序范式,它是意气风发种能够创制和调节造进度序的前后相继。模版元编制程序完全分裂于普通的运维期程序,它很出色,因为模版元程序的实行完全部都以在编译期,并且模版元程序决定的数量不可能是运转时变量,只好是编写翻译期常量,不可更改,此外它应用的语法元素也是一定有限,不能够选拔运转期的某些语法,比方if-else,for等语句都不能够用。由此,模版元编制程序须要广大技巧,平日须求类型重定义、枚举常量、世襲、模板偏特化等办法来合营,由此编写模版元编制程序相比复杂也正如困苦。

  今后C++11增产了一些模板元相关的天性,不仅可以够让我们编辑模版元程序变得更易于,还尤其进步了泛型编制程序的力量,例如type_traits让我们不必再重新发明轮子了,给大家提供了大气实惠的元函数,还提供了可变模板参数和tuple,让模版元编程“为虎添翼”。本文将向读者体现C++11中模版元编制程序常用的本领和现实性选拔。

若果想要编写翻译 crate,编写翻译器没有必要完全创设重视项,只须求它们的“元数据”(即类型、重视项、导出的列表卡塔尔国,元数据在编写翻译进度早期生成。从
Rust 1.38.0 初叶,Cargo
将依据那生机勃勃优势,风姿浪漫旦元数据策动稳当,就自动发轫营造正视的 crate。

$ rustup update stable

2.模版元基本概念

  模版元程序由元数据和元函数组成,元数据正是元编制程序能够操作的数据,即C++编写翻译器在编写翻译期能够操作的数量。元数据不是运维期变量,只可以是编写翻译期常量,不能够改过,平淡无奇的元数据有enum枚举常量、静态常量、基本项目和自定义类型等。

  元函数是模板元编制程序中用于操作管理元数据的“零器件”,能够在编写翻译期被“调用”,因为它的效果和格局和平运动作时的函数相符,而被叫做元函数,它是元编程中最重大的零器件。元函数实际表现为C++的叁个类、模板类或模板函数,它的家常花样如下:

template<int N, int M>
struct meta_func
{
    static const value = N+M;
}

  调用元函数到手value值:cout<<meta_func<1,
2>::value<<endl;

  meta_func的实施进度是在编写翻译期完结的,实际奉行顺序时,是绝非测算动作而是径直行使编写翻译期的测算结果的。元函数只管理元数据,元数据是编写翻译期常量和体系,所以下边的代码是编写翻译然则的:

int i = 1, j = 2;
meta_func<i, j>::value; //错误,元函数无法处理运行时普通数据

  模板元编制程序发生的源程序是在编译期实行的程序,由此它首先要规行矩步C++和模板的语法,可是它操作的对象不是运维时平时的变量,由此无法运用运维时的C++关键字(如if、else、for),可用的语法成分极其轻巧,最常用的是:

  • enum、static const,用来定义编写翻译期的莫西干发型常量;
  • typedef/using,用于定义元数据;
  • T、Args…,证明元数据类型;
  • template,主要用于定义元函数;
  • “::”,域运算符,用于分析类型成效域获取总计结果(元数据)。

要是模板元编制程序中供给if-else、for等逻辑时该咋做呢?

模板元中的if-else能够经过type_traits来促成,它不仅能够在编写翻译期做剖断,还足以做总结、查询、调换和选取。

模板元中的for等逻辑能够经过递归、重载、和模板特化(偏特化)等方法完结。

上面来探问C++11提供的模板元根基库type_traits。

虽说这种更换对单个 crate
的编译未有其余影响,但因此测量试验,拿到了一些告诉,对于涉及三个 crate
的构建,可为干净的和优化的营造带给差不离 10~五分一 的编写翻译速度
。而其余的纠正并超少,速度的增加决计于运营创设的硬件。

抑或直接从履新页面下载。

3.type_traits

  type_traits是C++11提供的沙盘模拟经营元根底库,通过type_traits可以兑以往编写翻译期总括、查询、决断、调换和接受,提供了模版元编制程序必要的局地常用元函数。下边来探问些为主的type_traits的主导用法。

  最简便的三个type_traits是概念编译期常量的元函数integral_constant,它的概念如下:

template< class T, T v >
struct integral_constant;

  依靠那一个大致的trait,大家得以很方便地定义编写翻译期常量,比如定义叁个值为1的int常量能够如此定义:

using one_type = std::integral_constant<int, 1>;

或者

template<class T>
struct one_type : std::integral_constant<int, 1>{};

  获取常量则透过one_type::value来获取,这种概念编译期常量的措施比较C++98/03要简明,在C++98/03中定义编写翻译期常量日常是如此定义的:

template<class T>
struct one_type
{
    enum{value = 1};
};

template<class T>
struct one_type
{
    static const int value = 1;
};

  能够见见,通过C++11的type_traits提供的一个简易的integral_constant就能够很方便的概念编写翻译期常量,而没有要求再去通过定义enum和static
const变量情势去定义编写翻译期常量了,那也为定义编写翻译期常量提供了其它生龙活虎种方法。C++11的type_traits已经提供了编写翻译期的true和false,是通过integral_constant来定义的:

typedef  integral_constant<bool, true> true_type;
typedef  integral_constant<bool, false> false_type;

  除了这个基本的元函数之外,type_traits还提供了充裕的元函数,譬喻用来编译期判定的元函数:

#include <iostream> #include <type_traits> int main() {
std::cout << “int: ” << std::is_const<int>::value
<< std::endl; std::cout << “const int: ” <<
std::is_const<const int>::value << std::endl;
//剖断类型是还是不是同样 std::cout<< std::is_same<int,
int>::value<<“n”;// true std::cout<<
std::is_same<int, unsignedint>::value<<“n”;// false
//添加、移除const cout << std::is_same<const int,
add_const<int>::type>::value << endl; cout <<
std::is_same<int, remove_const<const int>::type>::value
<< endl; //加多引用 cout << std::is_same<int&,
add_lvalue_reference<int>::type>::value << endl; cout
<< std::is_same<int&&,
add_rvalue_reference<int>::type>::value << endl;
//取公共项目 typedef std::common_type<unsigned char, short,
int>::type NumericType; cout << std::is_same<int,
NumericType>::value << endl; return 0; }

  type_traits还提供了编写翻译期选取traits:std::conditional,它在编写翻译期根据八个推断式采纳八个类型中的三个,和原则表达式的语义相近,近似于多个安慕希表明式。它的原型是:

template< bool B, class T, class F >
struct conditional;

用法比较容易:

#include <iostream>
#include <type_traits>

int main() 
{
    typedef std::conditional<true,int,float>::type A;               // int
    typedef std::conditional<false,int,float>::type B;              // float

    typedef std::conditional<(sizeof(long long) >sizeof(long double)),
    long long, long double>::type max_size_t;

    cout<<typeid(max_size_t).name()<<endl;  //long double
}

  此外八个常用的type_traits是std::decay(朽化卡塔尔国,它对于日常档期的顺序来讲std::decay(朽化)是移除引用和cv符,大大简化了笔者们的书写。除了普通等级次序之外,std::decay还能用于数组和函数,具体的转变准绳是这般的:

  先移除T类型的引用,得到类型U,U定义为remove_reference<T>::type。

  • 如果is_array<U>::value为
    true,改过类型type为remove_extent<U>::type *。
  • 否则,如果is_function<U>::value为
    true,订正类型type将为add_pointer<U>::type。
  • 再不,纠正类型type为 remove_cv<U>::type。

std::decay的主干用法:

typedef std::decay<int>::type A;           // int
typedef std::decay<int&>::type B;          // int
typedef std::decay<int&&>::type C;         // int
typedef std::decay<constint&>::type D;    // int
typedef std::decay<int[2]>::type E;        // int*
typedef std::decay<int(int)>::type F;      // int(*)(int)

  std::decay除了移除普通品种的cv符的法力之外,还足以将函数类型变换为函数指针类型,进而将函数指针变量保存起来,以便在背后延迟施行,举个例子上边包车型客车事例。

template<typename F>
struct SimpFunction
{
    using FnType = typename std::decay<F>::type;//先移除引用再添加指针

    SimpFunction(F& f) : m_fn(f){}

    void Run()
    {
        m_fn();
    }

    FnType m_fn;
};

  借使要保留输入的函数,则先要获取函数对应的函数指针类型,那时就足以用std::decay来获得函数指针类型了,using
FnType = typename
std::decay<F>::type;完结函数指针类型的定义。type_traits还提供了拿到可调用对象回来类型的元函数:std::result_of,它的着力用法:

int fn(int) {return int();}                            // function
typedef int(&fn_ref)(int);                             // function reference
typedef int(*fn_ptr)(int);                             // function pointer
struct fn_class { int operator()(int i){return i;} };  // function-like class

int main() {
  typedef std::result_of<decltype(fn)&(int)>::type A;  // int
  typedef std::result_of<fn_ref(int)>::type B;         // int
  typedef std::result_of<fn_ptr(int)>::type C;         // int
  typedef std::result_of<fn_class(int)>::type D;       // int
}

  type_traits还提供了三个很有用的元函数std::enable_if,它应用SFINAE(substitude
failure is not an
error卡塔尔天性,根据条件接收重载函数的元函数std::enable_if,它的原型是:

template<bool B, class T = void> struct enable_if;

  根据enable_if的字面意思就足以领略,它使得函数在认清标准B仅仅为true时才有效,它的骨干用法:

template <class T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t)
{
    return t;
}
auto r = foo(1); //返回整数1
auto r1 = foo(1.2); //返回浮点数1.2
auto r2 = foo(“test”); //compile error

  在上面包车型地铁事例中对模板参数T做了约束,即只可以是arithmetic(整型和浮点型)类型,倘使为非arithmetic类型,则编写翻译不经过,因为std::enable_if只对满足判别式条件的函数有效,对别的函数无效。

  能够透过enable_if来落到实处编写翻译期的if-else逻辑,比方下边的事例通过enable_if和原则判别式来将入参分为两大类,进而满意全体的入参类型:

template <class T>
typename std::enable_if<std::is_arithmetic<T>::value, int>::type foo1(T t)
{
    cout << t << endl;
    return 0;
}

template <class T>
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type foo1(T &t)
{
    cout << typeid(T).name() << endl;
    return 1;
}

  对于arithmetic类型的入参则重临0,对于非arithmetic的类型则赶回1,通过arithmetic将有所的入参类型分成了两大类进行拍卖。从上边的事例还足以看看,std::enable_if可以完成强盛的重载机制,因为平日来说必须是参数分歧技巧重载,假诺唯有重返值不一致是不可能重载的,而在地方的例证中,再次回到类型相似的函数都能够重载。

  C++11的type_traits提供了近百个在编写翻译期总结、查询、决断、调换和抉择的元函数,为大家编辑元程序提供了相当的大的福利。假若说C++11的type_traits让模版元编制程序变得轻易,那么C++11提供的可变模板参数和tuple则更为坚实了模版元编制程序。

mem::{uninitialized, zeroed}

此版本的亮点包罗:

4.可变模板参数

  C++11的可变模版参数(variadic
templates)是C++11小幅扩大的最有力的风味之风度翩翩,它对参数举办了冲天泛化,它能表示0到自由个数、放肆档次的参数。关于它的用法和利用手艺读者能够参照他事他说加以侦察作者在技术员二〇一六年二月A上的文章:泛化之美–C++11可变模版参数的妙用,这里不再赘述,这里将在体现的什么依据可变模板参数达成部分编译期算法,举例获取最大值、决断是或不是带有了有些项目、依据目录查找类型、获取项目标目录和遍历类型等算法。完毕这几个算法须要组合type_traits或其余C++11特征,上边来会见那一个编写翻译期算法是何许兑现的。

  编写翻译期从三个整形体系中获取最大值:

//获取最大的整数
template <size_t arg, size_t... rest>
struct IntegerMax;

template <size_t arg>
struct IntegerMax<arg> : std::integral_constant<size_t, arg>
{
};

template <size_t arg1, size_t arg2, size_t... rest>
struct IntegerMax<arg1, arg2, rest...> : std::integral_constant<size_t, arg1 >= arg2 ? IntegerMax<arg1, rest...>::value :
    IntegerMax<arg2, rest...>::value >
{
};

  那一个Integer马克斯的贯彻利用了type_traits中的std::integral_const,它在实行参数包的进度中,不断的可比,直到全体的参数都相比完,最后std::integral_const的value值即为最大值。它的使用很简短:

cout << IntegerMax<2, 5, 1, 7, 3>::value << endl;
//value为7

  我们得以在IntegerMax的根基上轻便的实现获取最大内部存储器对齐值的元函数马克斯Align。

  编译期获取最大的align:

template<typename... Args>
struct MaxAlign : std::integral_constant<int, IntegerMax<std::alignment_of<Args>::value...>::value>{};
cout << MaxAlign<int, short, double, char>::value << endl; //value为8
    编译判断是否包含了某种类型:
template < typename T, typename... List >
struct Contains;

template < typename T, typename Head, typename... Rest >
struct Contains<T, Head, Rest...>
    : std::conditional< std::is_same<T, Head>::value, std::true_type, Contains<T, Rest... >> ::type{};

template < typename T >
struct Contains<T> : std::false_type{};
用法:cout<<Contains<int, char, double, int, short>::value<<endl; //输出true

  这么些Contains的得以达成选择了type_traits的std::conditional、std::is_same、std::true_type和std::false_type,它的兑现思路是在进行参数包的进度中穿梭的可比类型是或不是后生可畏致,如若豆蔻梢头致则设置值为true,不然设置为false。

        编写翻译期获取项指标目录:

template < typename T, typename... List >
struct IndexOf;

template < typename T, typename Head, typename... Rest >
struct IndexOf<T, Head, Rest...>
{
    enum{ value = IndexOf<T, Rest...>::value+1 };
};

template < typename T, typename... Rest >
struct IndexOf<T, T, Rest...>
{
    enum{ value = 0 };
};

template < typename T >
struct IndexOf<T>
{
    enum{value = -1};
};

  用法:cout<< IndexOf<int, double, short, char, int,
float>::value<<endl; //输出3

  那几个IndexOf的完毕比较简单,在实行参数包的进程中看是或不是合营到特化的IndexOf<T,
T,
Rest…>,假设匹配上则甘休递归将早前的value累加起来获得指标项指标目录地点,不然将value加1,假使全数的类型中都从未相应的门类则赶回-1;

  编写翻译期根据目录地方查找类型:

template<int index, typename... Types>
struct At;

template<int index, typename First, typename... Types>
struct At<index, First, Types...>
{
    using type = typename At<index - 1, Types...>::type;
};

template<typename T, typename... Types>
struct At<0, T, Types...>
{
    using type = T;
};
    用法:
using T = At<1, int, double, char>::type;
    cout << typeid(T).name() << endl; //输出double

  At的兑现比较轻便,只要在拓宽参数包的长河中,不断的将索引依次减少至0时终结就可以获取对应索引地点的项目。接下来看看怎么样在编写翻译期遍历类型。

template<typename T>
void printarg()
{
    cout << typeid(T).name() << endl;
}

template<typename... Args>
void for_each() 
{
    std::initializer_list<int>{(printarg<Args>(), 0)...};
}
用法:for_each<int,double>();//将输出int double

  这里for_each的完结是通过开头化列表和逗号表明式来遍历可变模板参数的。

  能够见到,依赖可变模板参数和type_traits以致模板偏特化和递归等艺术大家能够兑现部分一蹴而就的编译期算法,这几个算法为我们编辑应用层级其他代码奠定了根底,前边模板元编制程序的切实接收元帅会用到那一个元函数。

  C++11提供的tuple让大家编辑模版元程序变得越来越灵敏了,在必然水平上加强了C++的泛型编制程序技巧,上面来看看tuple怎么样接纳于元程序中的。

从 1.38 起初,rustc 将为使用 mem::uninitialized 或 mem::zeroed
错误伊始化的狭义类提供 lint。

Future 天性已平稳

有了这种牢固,官方表示期望提供主要的
crates、库和生态系统时间来希图 async / .await

5.tuple与模版元

  C++11的tuple自己正是八个可变模板参数组成的元函数,它的原型如下:

template<class…Types>
class tuple;

  tuple在模板元编制程序中的一个接收场景是将可变模板参数保存起来,因为可变模板参数无法向来作为变量保存起来,须要依附tuple保存起来,保存之后再在供给的时候经过有个别花招将tuple又转移为可变模板参数,这些进程有一点点形似于化学中的“氧化还原反应”。看看下边包车型客车例证中,可变模板参数和tuple是什么相互调换的:

//定义整形序列
template<int...>
struct IndexSeq{};

//生成整形序列
template<int N, int... Indexes>
struct MakeIndexes : MakeIndexes<N - 1, N - 1, Indexes...>{};

template<int... indexes>
struct MakeIndexes<0, indexes...>{
    typedef IndexSeq<indexes...> type;
};

template<typename... Args>
void printargs(Args... args){
    //先将可变模板参数保存到tuple中
    print_helper(typename MakeIndexes<sizeof... (Args)>::type(), std::make_tuple(args...));
}

template<int... Indexes, typename... Args>
void print_helper(IndexSeq<Indexes...>, std::tuple<Args...>&& tup){
    //再将tuple转换为可变模板参数,将参数还原回来,再调用print
    print(std::get<Indexes>(tup)...); 
}
template<typename T>
void print(T t)
{
    cout << t << endl;
}

template<typename T, typename... Args>
void print(T t, Args... args)
{
    print(t);
    print(args...);
}

  用法:printargs(1, 2.5, “test”); //将输出1 2.5 test

  上边的事例print实际上是出口可变模板参数的剧情,具体做法是先将可变模板参数保存到tuple中,然后再经过元函数MakeIndexes生成一个整形连串,这一个整形连串就是IndexSeq<0,1,2>,整形系列代表了tuple申月素的目录,生成整形体系之后再调用print_helper,在print_helper中张开那么些整形种类,张开的长河中根据实际的目录从tuple中赢得相应的要素,最终将从tuple中抽出来的成分构成贰个可变模板参数,进而实现了tuple“还原”为可变模板参数,最后调用print打字与印刷可变模板参数。

  tuple在模板元编制程序中的其它三个采用途景是用来达成部分编写翻译期算法,举例大面积的遍历、查找和统一等算法,达成的思绪和可变模板参数完毕的编写翻译期算法相同,关于tuple相关的算法,读者能够参见小编在github上的代码:

  上边来拜望模版元的切切实实行使。

#[deprecated] macros

alloc crates 已稳定

在 1.36.0
在此之前,标准库由 std、 core和 proc_macro重新组合。主题包提供了根基效,如 Iterator 和Copy ,可以在 #![no_std] 景况中行使,因为它从不强加任何供给。同不日常候, std crate
提供相同 Box<T> 和 OS 功用的门类,但须求全局分配器和任何 OS
作用作为回报。

从 Rust 1.36.0
开始,std 的一些信任于大局分配器,举例 Vec<T>当今能够在 alloc crate
中使用。

6.模版元的施用

  我们将展现什么通过沙盘模拟经营元来完结function_traits和Vairant类型。

  function_traits用来赢得函数语义的可调用对象的局地质量,譬喻函数类型、重回类型、函数指针类型和参数类型等。上面来拜谒哪些兑现function_traits。

template<typename T>
struct function_traits;

//普通函数
template<typename Ret, typename... Args>
struct function_traits<Ret(Args...)>
{
public:
    enum { arity = sizeof...(Args) };
    typedef Ret function_type(Args...);
    typedef Ret return_type;
    using stl_function_type = std::function<function_type>;
    typedef Ret(*pointer)(Args...);

    template<size_t I>
    struct args
    {
        static_assert(I < arity, "index is out of range, index must less than sizeof Args");
        using type = typename std::tuple_element<I, std::tuple<Args...>>::type;
    };
};

//函数指针
template<typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...)> : function_traits<Ret(Args...)>{};

//std::function
template <typename Ret, typename... Args>
struct function_traits<std::function<Ret(Args...)>> : function_traits<Ret(Args...)>{};

//member function
#define FUNCTION_TRAITS(...) 
    template <typename ReturnType, typename ClassType, typename... Args>
    struct function_traits<ReturnType(ClassType::*)(Args...) __VA_ARGS__> : function_traits<ReturnType(Args...)>{}; 

FUNCTION_TRAITS()
FUNCTION_TRAITS(const)
FUNCTION_TRAITS(volatile)
FUNCTION_TRAITS(const volatile)

//函数对象
template<typename Callable>
struct function_traits : function_traits<decltype(&Callable::operator())>{};

  由于可调用对象大概是数见不鲜的函数、函数指针、lambda、std::function和分子函数,所以我们需求针对那么些项目分别做偏特化。当中,成员函数的偏特化微微复杂一点,因为涉及到cv符的拍卖,这里通过定义一个宏来消除重复的沙盘模拟经营类定义。参数类型的获得大家是依赖tuple,将参数调换为tuple类型,然后依照目录来获得相应品种。它的用法比较容易:

template<typename T>
void PrintType()
{
    cout << typeid(T).name() << endl;
}
int main()
{
    std::function<int(int)> f = [](int a){return a; };
    PrintType<function_traits<std::function<int(int)>>::function_type>(); //将输出int __cdecl(int)
    PrintType<function_traits<std::function<int(int)>>::args<0>::type>();//将输出int
    PrintType<function_traits<decltype(f)>::function_type>();//将输出int __cdecl(int)
}

  有了那么些function_traits和前面达成的片段元函数,大家就可以方便的兑现叁个“万能类型”—Variant,Variant实际上一个泛化的品类,那些Variant和boost.variant的用法近似。boost.variant的大旨用法如下:

typedef variant<int,char, double> vt;
vt v = 1;
v = 'a';
v = 12.32;

  这一个variant能够承当已经定义的那多少个类型,看起来有一点点相似于c#和java中的object类型,实际上variant是擦除了项目,要赢得它的实在类型的时候就稍显麻烦,须求通过boost.visitor来访谈:

澳门新葡萄京官网首页 1struct
VariantVisitor : public boost::static_visitor<void> { void
operator() (int a) { cout << “int” << endl; } void
operator() (short val) { cout << “short” << endl; } void
operator() (double val) { cout << “double” << endl; } void
operator() (std::string val) { cout << “string” << endl; }
}; boost::variant<int,short,double,std::string> v = 1;
boost::apply_visitor(visitor, v); //将输出int View Code

  通过C++11模板元完毕的Variant将更正值的获得,将得到实际值的艺术改为停放的,即通过上面包车型大巴不二等秘书籍来访问:

typedef Variant<int, double, string, int> cv;
cv v = 10;
v.Visit([&](double i){cout << i << endl; }, [](short i){cout << i << endl; }, [=](int i){cout << i << endl; },[](const string& i){cout << i << endl; });//结果将输出10

  这种方式更有益于直观。Variant的落到实处须要依靠前文中完结的一些元函数MaxInteger、MaxAlign、Contains和At等等。下边来拜望Variant完成的机要代码,完整的代码请读者参照他事他说加以调查我在github上的代码

澳门新葡萄京官网首页 2template<typename…
Types> class Variant{ enum{ data_size =
IntegerMax<sizeof(Types)…>::value, align_size =
MaxAlign<Types…>::value }; using data_t = typename
std::aligned_storage<data_size, align_size>::type; public:
template<int index> using IndexType = typename At<index,
Types…>::type; Variant(void) :m_typeIndex(typeid(void)){}
~Variant(){ Destroy(m_typeIndex, &m_data); }
Variant(Variant<Types…>&& old) : m_typeIndex(old.m_typeIndex){
Move(old.m_typeIndex, &old.m_data, &m_data); } Variant(const
Variant<Types…>& old) : m_typeIndex(old.m_typeIndex){
Copy(old.m_typeIndex, &old.m_data, &m_data); } template <class T,
class = typename std::enable_if<Contains<typename
std::remove_reference<T>::type, Types…>::value>::type>
Variant(T&& value) : m_typeIndex(typeid(void)){ Destroy(m_typeIndex,
&m_data); typedef typename std::remove_reference<T>::type U;
new(&m_data) U(std::forward<T>(value)); m_typeIndex =
type_index(typeid(U)); } template<typename T> bool Is() const{
return (m_typeIndex == type_index(typeid(T))); } template<typename
T> typename std::decay<T>::type& Get(){ using U = typename
std::decay<T>::type; if (!Is<U>()) { cout <<
typeid(U).name() << ” is not defined. ” << “current type is
” << m_typeIndex.name() << endl; throw std::bad_cast(); }
return *(U*)(&m_data); } template<typename F> void Visit(F&&
f){ using T = typename Function_Traits<F>::template
arg<0>::type; if (Is<T>()) f(Get<T>()); }
template<typename F, typename… Rest> void Visit(F&& f, Rest&&…
rest){ using T = typename Function_Traits<F>::template
arg<0>::type; if (Is<T>()) Visit(std::forward<F>(f));
else Visit(std::forward<Rest>(rest)…); } private: void
Destroy(const type_index& index, void * buf){
std::initializer_list<int>{(Destroy0<Types>(index, buf),
0)…}; } template<typename T> void Destroy0(const type_index&
id, void* data){ if (id == type_index(typeid(T)))
reinterpret_cast<T*>(data)->~T(); } void Move(const
type_index& old_t, void* old_v, void* new_v) {
std::initializer_list<int>{(Move0<Types>(old_t, old_v,
new_v), 0)…}; } template<typename T> void Move0(const
type_index& old_t, void* old_v, void* new_v){ if (old_t ==
type_index(typeid(T))) new
(new_v)T(std::move(*reinterpret_cast<T*>(old_v))); } void
Copy(const type_index& old_t, void* old_v, void* new_v){
std::initializer_list<int>{(Copy0<Types>(old_t, old_v,
new_v), 0)…}; } template<typename T> void Copy0(const
type_index& old_t, void* old_v, void* new_v){ if (old_t ==
type_index(typeid(T))) new (new_v)T(*reinterpret_cast<const
T*>(old_v)); } private: data_t m_data; std::type_index
m_typeIndex;//类型ID }; View
Code

  完毕Variant首先供给定义叁个丰硕大的缓冲区用来存放在差别的连串的值,这一个缓类型冲区实际上纵然用来擦除类型,不相同的类型都通过placement
new在此个缓冲区上制造对象,因为项目长度不一样,所以须要考虑内部存款和储蓄器对齐,C++11正好提供了内存对齐的缓冲区aligned_storage:

template< std::size_t Len, std::size_t Align = /*default-alignment*/ >
struct aligned_storage;

  它的首先个参数是缓冲区的尺寸,第二个参数是缓冲区内部存款和储蓄器对齐的抑扬顿挫,由于Varaint能够选拔六连串型,所以大家须求获得最大的品种长度,保险缓冲区丰盛大,然后还要得到最大的内部存款和储蓄器对齐大小,这里大家经过前面达成的MaxInteger和MaxAlign即可了,Varaint中内部存款和储蓄器对齐的缓冲区定义如下:

enum
{
    data_size = IntegerMax<sizeof(Types)...>::value,
    align_size = MaxAlign<Types...>::value
};
using data_t = typename std::aligned_storage<data_size, align_size>::type; //内存对齐的缓冲区类型

  其次,我们还要促成对缓冲区的组织、拷贝、析构和移动,因为Variant重新赋值的时候须求将缓冲区中原本的体系析构掉,拷贝布局和活动布局时则必要拷贝和移动。这里以析构为例,大家须求依赖当前的type_index来遍历Variant的装有系列,找到相应的档期的顺序然后调用该类型的析构函数。

void Destroy(const type_index& index, void * buf)
    {
        std::initializer_list<int>{(Destroy0<Types>(index, buf), 0)...};
    }

    template<typename T>
    void Destroy0(const type_index& id, void* data)
    {
        if (id == type_index(typeid(T)))
            reinterpret_cast<T*>(data)->~T();
    }

  这里,大家经过伊始化列表和逗号表明式来开展可变模板参数,在开展的历程中查究对应的花色,假如找到了则析构。在Variant布局时还亟需留意多少个细节是,Variant不可能选取未有事情发生从前定义的体系,所以在组织Variant时,须要限定品种必得在预约义的品类范围当中,这里通过type_traits的enable_if来节制模板参数的花色。

template <class T,
    class = typename std::enable_if<Contains<typename std::remove_reference<T>::type, Types...>::value>::type> Variant(T&& value) : m_typeIndex(typeid(void)){
            Destroy(m_typeIndex, &m_data);
            typedef typename std::remove_reference<T>::type U;
            new(&m_data) U(std::forward<T>(value));
            m_typeIndex = type_index(typeid(U));
    }

  这里enbale_if的标准正是眼前达成的元函数Contains的值,当未有在预订义的类型中找到相应的门类时,即Contains再次回到false时,编写翻译期会报三个编译错误。

  最终还须要贯彻内置的Vistit效能,Visit的完毕须求先通过定义生机勃勃多重的访谈函数,然后再遍历这几个函数,遍历进度中,判别函数的首先个参数类型的type_index是或不是与当前的type_index相符,假设同样则获得当前项目标值。

template<typename F>
    void Visit(F&& f){
        using T = typename Function_Traits<F>::template arg<0>::type;
        if (Is<T>())
            f(Get<T>());
    }

    template<typename F, typename... Rest>
    void Visit(F&& f, Rest&&... rest){
        using T = typename Function_Traits<F>::template arg<0>::type;
        if (Is<T>())
            Visit(std::forward<F>(f));
        else
            Visit(std::forward<Rest>(rest)...);
    }

  Visit作用的完毕利用了可变模板参数和function_traits,通过可变模板参数来遍历一星罗棋布的拜访函数,遍历进度中,通过function_traits来获得第三个参数的类型,和Variant当前的type_index相符的则取值。为啥要拿会见谈函数第三个参数的种类呢?因为Variant的值是独一无二的,独有多少个值,所以获得的探访函数的第四个参数的类型正是Variant中存放的对象的骨子里类型。

#[deprecated] 属性最先是在 Rust 1.9.0 版本中引进的。Rust 1.38.0
扩大了质量,允许将其接受于宏,使用 deprecated 属性将宏标识为
deprecated。

MaybeUninit<T> 代替 mem::uninitialized

在 Rust
的最早版本中,mem::uninitialized 函数允许通过伪装已在品种 T 初步化值而不举行其它操作来绕过
Rust 的开首化检查。不过mem::uninitialized 基本上不可能准确运用,因为
Rust 编写翻译器假定值已正确起首化。

为了缓慢解决这种情景,在 Rust 1.36.0
中, MaybeUninit<T> 类型已经稳定。

并且,由于 MaybeUninit<T> 是更安全的替代方案,从 Rust 1.38
开头,函数 mem::uninitialized将被弃用。

7总结

  C++11中的一些特征举个例子type_traits、可变模板参数和tuple让模版元编制程序变得更简约也越来越强有力,模版元编制程序即使作用强盛,但也比较复杂,要用好模版元,供给大家转移观念方法,在调整大旨的答辩的基础上,再认真商讨模版元的有个别常用技能,那些技巧是有规律可循的,基本上都以由此重定义、递归和偏特化等花招来实现的,当大家对这个基本手艺很熟谙的时候再组成不断地实行,相信对模版元编制程序就会不负众望“相当了然”了。

 

正文曾刊登于《技师》二零一五年五月刊。转发请注解出处。

后记:本文的内容首要来源于自家在协作社内部培养锻练的一遍课程,因为不菲人对C++11模板元编程通晓得不深切,所以本身以为有必要拿出来共享一下,让越来越多的人看来,就收拾了刹那间发到程序猿杂志了,笔者深信读者看完之后对模版元会有完美通透到底的问询。

1.概述
模版元编制程序(template
metaprogram)是C++中最复杂也是威力最精锐的编制程序范式,它是大器晚成种能够创设和垄断(monopoly卡塔尔(قطر‎程序…

std::any::type_name

NLL 向后移植到 Rust 二〇一五

Rust 1.31.0 推出了
NLL(非词汇生命周期),这种语言的改善使借阅检查器变得愈加智能且更为客户本人。举个例子,能够这样写:

fn main() {
    let mut x = 5;
    let y = &x;
    let z = &mut x; // This was not allowed before 1.31.0. }

在 1.31.0 中,NLL 仅在 Rust 2018 中稳固下来,并承诺会被向后移植到 Rust
2014。Rust 1.36.0 完毕了那么些承诺,NLL 今后可用来 Rust 2015。

对于调试,获取项目标名称不常是平价的。举例,在通用代码中,你恐怕希望在运作时观察函数的品种参数被实例化的现实性品种,此版本能够运用
std::any::type_name 达到那生机勃勃须要:

叁个新的 HashMap<K, V> 实现

在 Rust 1.36.0 中, HashMap<K, V> 达成已被轮番为根据 SwissTable
设计的 hashbrown crate
中的达成。即使接口是同样的,但 HashMap<K, V> 实现今后平均更加快并且具备更低的内部存款和储蓄器费用。供给注意的是,与 hashbrown crate
不同,std 中的达成仍默感到 SipHash 1-3 散列算法。

fn gen_value<T: Default>() -> T {
    println!("Initializing an instance of {}", std::any::type_name::<T>());
    Default::default()
}

fn main() {
    let _: i32 = gen_value();
    let _: String = gen_value();
}

Cargo 的离线扶助

在 Rust 1.36 中,支持 --offline改换了 Cargo
的信赖解析算法,仅使用当地缓存的依附关系。要优先填充本地缓存以希图脱机,请使用
cargo fetch 命令,该命令将下载项目所需的有着信任项。

澳门新葡萄京官网首页 3输出结果:

库的改观

  •  dbg! 宏今后帮助八个参数

新的牢固性 API 包含:

  • task::Waker 和 task::Poll
  • VecDeque::rotate_left 和 VecDeque::rotate_right
  • Read::read_vectored 和 Write::write_vectored
  • Iterator::copied
  • BorrowMut<str> for String
  • str::as_mut_ptr

详细的情况可查阅发通知示。

(文/开源中中原人民共和国State of Qatar    

Initializing an instance of i32
Initializing an instance of alloc::string::String

澳门新葡萄京官网首页 4澳门新葡萄京官网首页 5库的更换

  • slice::{concat,connect,join} 以后支撑 &[T] 和 &T
  • *const T 和 *mut T 以往贯彻 maker::Unpin
  • Arc<[T]> 和 Rc<[T]> 实现 FormIterator<T>
  • iter::StepBy,Peekble,Take 实现 DoubleEndedIterator

此外,以下那一个效应已经平静:

  • Duration::div_duration_f32 和 Duration::div_duration_f64
  • <*const T>::cast 和 <*mut T>::cast
  • Duration::as_secs_f32 和 Duration::as_secs_f64
  • Duration::div_f32 和 Duration::div_f64
  • Duration::from_secs_f32 和 Duration::from_secs_f64
  • Duration::mul_f32 和 Duration::mul_f64 

拿到 1.38 版本方式如下:

rustup update stable

澳门新葡萄京官网首页 6澳门新葡萄京官网首页 7端详见发公布告:

(文/开源中中原人民共和国State of Qatar