c++新标准有用的语法特性


最近读了一些开源库,发现开源库都会尽量使用最新的语言特性。大量开源库使用c++的新标准,一些新语法,新的模板元编程技术。如果不懂新标准增加的语法的话,这些开源库是很难读懂的,因此也很难用到自己的项目中。这也从侧面反映了,如果要写好一个底层库或者框架,需要对语言的掌握到一定的程度。这些新标准的加入,简化了一些编码工作。比如之前几十行代码解决的问题,现在只要几行就可以解决了;扩展了语言特性,使其看起来更加“现代化”;解决了之前不能解决或者需要很复杂的技巧才能解决的一些问题;

下面就对最近在读开源库过程中遇到的一些新的语法特性做一个总结。

代码片段1

template<typename T, typename = void>
inline constexpr bool IsReflected_v = false;

特性1:变量模板

定义变量使用模板的形式,可以一次定义多个同类变量。

c++14 新特性变量模板,之前参数化变量的实现方法为:1:类模板的静态数据成员,2:constexpr函数模板

特性2:头文件变量定义

inline关键字新用法,在变量定义前加入inline关键字,即使这个变量被多个头文件包含,也可编译通过。

之前,需要在cpp中定义,然后在h中使用extern关键字。

代码片段2

template<typename T, typename F>
constexpr auto forEachField(T&& obj, F&& f) {
	return forEachField(
        std::forward<T>(obj),
		std::forward<F>(f),
		std::make_index_sequence<std::decay_t<T>::_field_count_>{});
}

constexpr函数

在函数前加constexpr,这样定义的函数,在编译的时候就能得到其返回值。

万能引用

在模板函数中*&&*代表的是万能引用,万能引用一般与如下的完美转发配合使用。

std::forward

完美转发:按照参数原来的类型转发到另一个函数,因为std::forward可以保存参数的左值或右值特性。

template<class T> 
void wrapper(T&& arg) 
{   
	foo(std::forward<T>(arg)); 
}

make_index_sequence

c++14加入的新特性,std::make_inedex_sequence的作用是产生一个0,1,2,3,…,N-1的数列,该数列在编译期已确定。该特性一般在函数模板中使用,与index_sequence配合使用。

std::decay_t

为类型T应用从左值到右值(lvalue - to - rvalue)、数组到指针(array - to - pointer)和函数到指针(function - to - pointer)的隐式转换。转换将移除类型T的cv限定符(const和volatile限定符),并定义结果类型为成员decay::type的类型。这种转换很类似于当函数的所有参数按值传递时发生转换。

代码片段3

	template<typename T, typename F, size_t... Is>
	constexpr auto forEachField(T&& obj, F&& f, std::index_sequence<Is...>) 
    {
		using TDECAY = std::decay_t<T>;

		if constexpr (std::is_same_v<decltype(f(std::declval<DummyFieldInfo>())), Result>) 
        {
			Result res{ Result::SUCCESS };
			(void)(((res = f(typename TDECAY::template FIELD<T, Is>
				(std::forward<T>(obj)) )) == Result::SUCCESS) && ...);
			return res;
		}
		else 
        {
			(f(typename TDECAY::template FIELD<T, Is>(std::forward<T>(obj))), ...);
		}
	}

可变参数模板

c++11引入了可变参数模板。如上的代码片段中,模板参数包含size_t… Is,代表可变参数模板。在类型后面跟… ,即为可变参数模板。本例中可变参数为一组变长的常数,也可以为多个同类型的参数。

index_sequence

代表一组数列,在本例中作为可变参数模板。

C++可变参数模板的展开方式 …

上面的代码中:

(void)(((res = f(typename TDECAY::template FIELD<T, Is> 
	(std::forward<T>(obj)) )) == Result::SUCCESS) && ...);

&& …代表参数展开,代表前面的代码段

((res = f(typename TDECAY::template FIELD<T, Is>(std::forward<T>(obj)) )) == Result::SUCCESS)

展开N次,并用&&连接。

这种方式是c++17引入的折叠表达式,分为左折叠和右折叠。

  • args + …表示:(a + (b + (c + d))),这叫做右折叠

  • … + args表示:(((a + b) + c) + d),这叫做左折叠

  • args + … + init表示:(a + (b + (c + (d + init)))),这叫做右折叠

  • init + … + args表示:((((a + init) + b) + c) + d),这叫做左折叠

在c++11中,要展开参数,有两种方式,一种是递归展开,另一种是使用逗号表达式。

模板中类型转换

typename std::decay_t<T>::template FIELD<T, Is>

取到T类型中指定的模板类型FIELD

std::is_same_v

比较两个类型是否相同,在编译期可知道。和if constexpr配合使用。使函数在编译期就确定执行流程。

代码片段4

template<typename T>
struct CompoundDeserializeTraits<T
    , std::enable_if_t<PrimitiveDeserializeTraits<T>::value>>
{
    template<typename ELEM_TYPE>
    static Result deserialize(T& obj, ELEM_TYPE node)
    {
        if (!node.isValid())
        {
            return Result::ERR_MISSING_FIELD;
        }
        return PrimitiveDeserializeTraits<T>::deserialize(obj, node.getValueText());
    }
};

std::enable_if_t

当PrimitiveDeserializeTraits::value为真时,优先匹配这个模板。

详细解释:

基础模板为:

template<typename T, typename = void>
struct CompoundDeserializeTraits;

判断所用的基础类型为:

template<typename T, typename = void>
struct PrimitiveDeserializeTraits : std::false_type {};

PrimitiveDeserializeTraits::value默认为假。

判断所有的基础类型偏特为:

//数字类型特化
template<typename Number>
struct PrimitiveDeserializeTraits<Number, std::enable_if_t<std::is_arithmetic_v<Number>>>
    : std::true_type
{
    static Result deserialize(Number& num, std::optional<std::string> valueText) 
    {
        if (!valueText.has_value()) 
        { 
            return Result::ERR_EXTRACTING_FIELD;
        }
        // do not treat int8_t/uint8_t as char type
        if constexpr (std::is_same_v<Number, int8_t> || std::is_same_v<Number, uint8_t>) 
        {
            num = std::stol(*valueText, nullptr, detail::isHex(*valueText) ? 16 : 10);
            return Result::SUCCESS;
        }
        else 
        {
            std::stringstream ss;
            ss << *valueText;
            if (detail::isHex(*valueText)) { ss << std::hex; }
            ss >> num;
            return ss.fail() ? Result::ERR_EXTRACTING_FIELD : Result::SUCCESS;
        }
    }
};

当传入的是数类型时,PrimitiveDeserializeTraits::value为真。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仟人斩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值