首页 > 编程笔记 > C++笔记 阅读:13

C++类型特征的用法(附带实例)

模板元编程是语言的一个强大特性,它使我们能够编写和重用适用于所有类型的通用代码。然而,在实践中,对于不同的类型,由于意图、语义正确性、性能或其他原因,通用代码通常需要以不同的方式工作,或者根本不工作。

例如,你可能希望针对 POD 类型和非 POD 类型实现不同的通用算法,或者希望仅用整型实例化函数模板。C++11 提供了一组类型特征(type traits)来帮助实现这一点。

类型特征基本上是提供其他类型信息的元类型。类型特征库包含一长串特征,它们用于获取类型属性(例如检查类型是否是整型或两个类型是否想同),也用于执行类型转换(例如删除 const 和 volatile 限定符或添加指向类型的指针)。

在本节中,我们将研究类型特征是什么以及它们是如何工作的。

C++类型特征的使用

C++11 引入的所有类型特征都能在 <type_traits> 头文件的 std 命名空间中找到。下面列出了使用类型特征来实现各种设计目标的各种情况:

1) 使用 enable_if 为类型定义前置条件,函数模板可以使用这些类型实例化:
template <typename T,typename = typename std::enable_if_t<std::is_arithmetic_v<T> > >
T multiply(T const t1, T const t2)
{
    return t1 * t2;
}

auto v1 = multiply(42.0, 1.5);    // OK
auto v2 = multiply("42"s, "1.5"s); // error

2) 使用 static_assert 确保满足不变量:
template <typename T>
struct pod_wrapper
{
    static_assert(std::is_standard_layout_v<T> && std::is_trivial_v<T>, "Type is not a POD!");

    T value;
};

pod_wrapper<int> if 42 };    // OK
pod_wrapper<std::string> s{ "42"s }; // error

3) 使用 std::conditional 在类型之间进行选择:
template <typename T>
struct const_wrapper
{
    typename std::conditional_t <std::is_const_v<T>, T, typename std::add_const_t<T>> const_type;
};

static_assert(std::is_const_v<const_wrapper<int>::const_type>);

static_assert(std::is_const_v<const_wrapper<int const>::const_type>);

4) 使用 constexpr if 使编译器能够根据实例化模板的类型生成不同的代码:
template <typename T>
auto process(T arg)
{
    if constexpr (std::is_same_v<T, bool>)
        return !arg;
    else if constexpr (std::is_integral_v<T>)
        return std::abs(arg);
    else
        return arg;
}

auto v1 = process(false); // v1 = true
auto v2 = process(42);   // v2 = -42
auto v3 = process(-42.0); // v3 = 42.0
auto v4 = process("42"s); // v4 = "42"

C++类型特征的工作原理

类型特征是提供关于类型的元信息或可用于修改类型的类。实际上有两种类型特征:
为了方便起见,这里提供了一个简短的摘要:
int process(int arg)
{
    return -arg;
}

类型特征是通过提供类模板和它的部分特化或完整特化来实现的。下面表示一些类型特征的概念实现:
1) is_void() 方法指示类型是否为 void。这使用了完整特化:
template <typename T>
struct is_void
{ static const bool value = false; };

template <>
struct is_void<void>
{ static const bool value = true; };

2) is_pointer() 方法指示类型是指向对象的指针还是指向函数的指针,这使用了部分特化:
template <typename T>
struct is_pointer
{ static const bool value = false; };

template <typename T>
struct is_pointer<T*>
{ static const bool value = true; };
需要注意的是,在 C++20 中,POD 类型的概念已被弃用,这也包括对 std::is_pod 类型特征的弃用。POD 类型是一种类型,它既是 trivial 类型(具有编译器提供的或显式默认的特殊成员,并占用连续的内存区域),又是标准布局类型(不包含语言特性的类,例如与 C语言不兼容的虚函数,并且所有成员具有相同的访问控制机制)。

因此,从 C++20 开始,更细粒度的 trivial 类型和标准布局类型概念是首选,这也意味着不应该再使用 std::is_pod,而应该分别使用 std::is_trivial 和 std::is_standard_layout。

相关文章