下面就对最近在读开源库过程中遇到的一些新的语法特性做一个总结。
template<typename T, typename = void>
inline constexpr bool IsReflected_v = false;
定义变量使用模板的形式,可以一次定义多个同类变量。
c++14 新特性变量模板,之前参数化变量的实现方法为:1:类模板的静态数据成员,2:constexpr函数模板
inline关键字新用法,在变量定义前加入inline关键字,即使这个变量被多个头文件包含,也可编译通过。
之前,需要在cpp中定义,然后在h中使用extern关键字。
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,这样定义的函数,在编译的时候就能得到其返回值。
在模板函数中*&&*代表的是万能引用,万能引用一般与如下的完美转发配合使用。
完美转发:按照参数原来的类型转发到另一个函数,因为std::forward可以保存参数的左值或右值特性。
template<class T>
void wrapper(T&& arg)
{
foo(std::forward<T>(arg));
}
c++14加入的新特性,std::make_inedex_sequence的作用是产生一个0,1,2,3,…,N-1的数列,该数列在编译期已确定。该特性一般在函数模板中使用,与index_sequence配合使用。
为类型T应用从左值到右值(lvalue - to - rvalue)、数组到指针(array - to - pointer)和函数到指针(function - to - pointer)的隐式转换。转换将移除类型T的cv限定符(const和volatile限定符),并定义结果类型为成员decay::type的类型。这种转换很类似于当函数的所有参数按值传递时发生转换。
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,代表可变参数模板。在类型后面跟… ,即为可变参数模板。本例中可变参数为一组变长的常数,也可以为多个同类型的参数。
代表一组数列,在本例中作为可变参数模板。
上面的代码中:
(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
比较两个类型是否相同,在编译期可知道。和if constexpr配合使用。使函数在编译期就确定执行流程。
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());
}
};
当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为真。