C++中几个常用的类型选择模板函数

std::enable_if<B, T>::type

如果编译期满足B,那么返回类型T,否则编译报错

std::conditional<B, T, F>::type

如果编译期满足B,那么返回类型T,否则返回类型F

下面是一个示例,展示如何使用 `std::conditional` 来选择返回类型:

cpp 复制代码
#include <iostream>
#include <type_traits>

template <typename T, typename U>
auto max(T a, U b) -> typename std::conditional<(sizeof(T) > sizeof(U)), T, U>::type {
  return (a > b) ? a : b;
}

int main() {
  int a = 5;
  double b = 3.14;

  auto result = max(a, b);
  std::cout << "Max value: " << result << std::endl;

  return 0;
}

在上面的示例中,`max` 函数使用了 `std::conditional` 来根据两个参数的类型选择返回类型。如果 `sizeof(T) > sizeof(U)`,则返回类型为 `T`,否则返回类型为 `U`。在 `main` 函数中,我们调用 `max` 函数并打印返回值,演示了根据条件选择返回类型的功能。

需要注意的是,`std::conditional` 是一个模板类,它提供了在编译时进行类型选择的能力。它在很多情况下可以用来实现类型萃取(type traits)和条件编译等功能。

std::integral_constant<T, value>

`std::integral_constant<T, value>` 是 C++ 标准库中的一个模板类,用于表示一个编译时常量的包装器。它接受两个参数:`T` 表示常量的类型,`value` 表示常量的值。

`std::integral_constant<bool, B>` 表示一个固定的(常量)布尔值,其类型为 `bool`,值为 `B`。

下面是一个示例,展示如何使用 `std::integral_constant` 来表示编译时的布尔常量:```cpp

cpp 复制代码
#include <iostream>
#include <type_traits>

int main() {
  std::integral_constant<bool, true> true_value;
  std::integral_constant<bool, false> false_value;

  std::cout << "true_value: " << true_value << std::endl;
  std::cout << "false_value: " << false_value << std::endl;

  std::cout << "true_value type: " << typeid(true_value).name() << std::endl;
  std::cout << "false_value type: " << typeid(false_value).name() << std::endl;

  std::cout << "true_value value: " << true_value() << std::endl;
  std::cout << "false_value value: " << false_value() << std::endl;

  return 0;
}

在上面的示例中,我们创建了 `true_value` 和 `false_value` 两个 `std::integral_constant` 实例,分别表示编译时的 `true` 和 `false` 布尔常量。我们使用 `std::cout` 打印了各个常量的值、类型和调用结果。

需要注意的是,`std::integral_constant<bool, B>` 实际上是一个包装器,它提供了类型信息和调用操作符(函数调用运算符),使得它在使用时可以像一个函数一样进行调用。调用 `std::integral_constant` 对象会返回其持有的常量值。

此外,C++ 标准库中的 `std::integral_constant` 还提供了一系列的成员类型和成员函数,如 `value_type`、`operator()`、`operator bool` 等,用于操作和查询包装的常量。这些成员函数和成员类型可以用于进行类型推导、模板元编程和编译时条件判断等用途。

下面看一个比较难的应用,将类型和自定义的数字联系起来,做一些类型判断:

cpp 复制代码
#include <iostream>
enum class type {
  none_type,
  // Integer types should go first,
  int_type,
  uint_type,
  long_long_type,
  ulong_long_type,
  int128_type,
  uint128_type,
  bool_type,
  char_type,
  last_integer_type = char_type,
  // followed by floating-point types.
  float_type,
  double_type,
  long_double_type,
  last_numeric_type = long_double_type,
  cstring_type,
  string_type,
  pointer_type,
  custom_type
};

// Maps core type T to the corresponding type enum constant.
template <typename T>
struct type_constant : std::integral_constant<type, type::custom_type> {};

#define FMT_TYPE_CONSTANT(Type, constant) \
  template <> \
  struct type_constant<Type>        \
      : std::integral_constant<type, type::constant> {}

FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(float, float_type);
FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);

constexpr bool is_integral_type(type t) {
  return t > type::none_type && t <= type::last_integer_type;
}
constexpr bool is_arithmetic_type(type t) {
  return t > type::none_type && t <= type::last_numeric_type;
}


int main()
{
    int a = 12;
    std::cout<< is_integral_type(type_constant<decltype(a)>::value)<<std::endl; //1
    bool* b = nullptr;
    std::cout<< is_integral_type(type_constant<decltype(b)>::value)<<std::endl; //0
}

std::underlying_type<T>::type

`std::underlying_type<T>::type` 是 C++ 标准库中的一个类型转换工具,它用于获取枚举类型 `T` 的底层类型。

`std::underlying_type` 是一个模板类,接受一个类型参数 `T`,它用于表示一个枚举类型。`std::underlying_type<T>::type` 表示枚举类型 `T` 的底层类型。

下面是一个示例,展示如何使用 `std::underlying_type<T>::type` 来获取枚举类型的底层类型:

cpp 复制代码
#include <iostream>
#include <type_traits>

enum class MyEnum : unsigned int {
  Value1,
  Value2,
  Value3
};

int main() {
  using UnderlyingType = std::underlying_type<MyEnum>::type;
  
  std::cout << "Underlying type of MyEnum: " << typeid(UnderlyingType).name() << std::endl;

  return 0;
}

在上面的示例中,我们定义了一个枚举类型 `MyEnum`,包含三个枚举值。然后,我们使用 `std::underlying_type<MyEnum>::type` 来获取 `MyEnum` 的底层类型,并将其赋值给 `UnderlyingType` 类型别名。最后,我们使用 `std::cout` 打印底层类型的类型信息。

在这个示例中,我们使用 `std::underlying_type` 来获取 `MyEnum` 的底层类型。`UnderlyingType` 类型别名被推导为 `unsigned int`,这是 `MyEnum` 底层的类型。

`std::underlying_type` 对于在编译时获取枚举类型的底层类型非常有用。它可以用于处理枚举类型的底层值,进行位操作或其他需要底层类型的操作。

std::remove_cv<remove_reference_t<T>>::type

移除类型T的各种限定符。

比如:const、volatile、左值引用T&。

std::is_same<T, T2>::value

std::is_same<T, T2>::value 是 C++ 标准库中的一个类型特性工具,用于检查类型 T 和类型 T2 是否相同。值得注意的是,cv限定符会让类型不同。

cpp 复制代码
#include <iostream>
#include <type_traits>

int main() {
  bool isSame = std::is_same<int, int>::value;
  std::cout << "isSame: " << isSame << std::endl; // true

  isSame = std::is_same<int, double>::value;
  std::cout << "isSame: " << isSame << std::endl; // false

  bool isSame = std::is_same<const int, int>::value;
  std::cout << "isSame: " << isSame << std::endl; // false

  isSame = std::is_same<volatile int, int>::value;
  std::cout << "isSame: " << isSame << std::endl; // false

  isSame = std::is_same<const int, volatile int>::value;
  std::cout << "isSame: " << isSame << std::endl; // false
  return 0;
}

std::is_base_of<T, D>::value

`std::is_base_of<compile_string, S>` 是 C++ 标准库中的一个模板类 `std::is_base_of` 的使用,用于检查类型 `S` 是否是类型 `compile_string` 的基类或派生类。

`std::is_base_of` 是一个类型特性(type trait),它提供了一种在编译时判断一个类型是否是另一个类型的基类或派生类的机制。它属于 C++ 类型特性库(type traits library)的一部分,位于 `<type_traits>` 头文件中。

在给定的代码中,`std::is_base_of<compile_string, S>` 用于检查类型 `S` 是否是类型 `compile_string` 的基类或派生类。它返回一个编译时常量值 `true` 或 `false`,表示 `S` 是否是 `compile_string` 的派生类。

具体的使用示例如下:

cpp 复制代码
// A base class for compile-time strings.
struct compile_string {};

template <typename S>
struct is_compile_string : std::is_base_of<compile_string, S> {};

template <typename S, std::enable_if<is_compile_string<S>::value>::value>
constexpr auto to_string_view(const S& s)
    -> basic_string_view<typename S::char_type> {
  return basic_string_view<typename S::char_type>(s);
}

在上面的示例中,我们使用 `std::is_base_of` 来检查 `derived_class` 是否是 `compile_string` 的派生类。根据返回的结果,我们输出相应的信息。

需要注意的是,`std::is_base_of` 是在编译时进行类型检查的,它只能用于判断类型之间的继承关系,而不能用于检查对象之间的关系。

std::declval<T>()

std::declval<S> 是 C++ 标准库中的一个模板函数 std::declval 的使用,用于获取类型 S 的一个虚拟值(引用)。

std::declval 是一个函数模板,它允许在不实际创建对象的情况下,获取一个类型的虚拟值。它位于 <utility> 头文件中。

在给定的代码中,std::declval<S> 用于获取类型 S 的一个虚拟值。它返回一个该类型的右值引用(Rvalue reference)。

std::declval 的主要用途是用于在编译时创建对类型的引用,以便在模板编程中进行类型推断。 它通常与 decltype 一起使用,用于声明返回类型或推断表达式的类型。需要注意的是,std::declval 用于创建虚拟值,**但不能用于实际访问该值。**它主要用于模板元编程中,用于推断类型和进行 SFINAE(Substitution Failure Is Not An Error)。

cpp 复制代码
template <typename S>
struct is_string
    : std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};

template <typename S, typename = void> struct char_t_impl {};
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
  using result = decltype(to_string_view(std::declval<S>()));
  using type = typename result::value_type;
};

// detail::to_string_view see below

template <typename Char, std::enable_if<is_char<Char>::value)>::value>
inline auto to_string_view(const Char* s) -> basic_string_view<Char> {
  return s;
}
template <typename Char, typename Traits, typename Alloc>
inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
    -> basic_string_view<Char> {
  return s;
}
template <typename Char>
constexpr auto to_string_view(basic_string_view<Char> s)
    -> basic_string_view<Char> {
  return s;
}
template <typename Char,
          std::enable_if<!std::is_empty<std_string_view<Char>>::value>::value>
inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
  return s;
}
template <typename S, std::enable_if<is_compile_string<S>::value>::value>
constexpr auto to_string_view(const S& s)
    -> basic_string_view<typename S::char_type> {
  return basic_string_view<typename S::char_type>(s);
}
void to_string_view(...);

比如上面,创建了一个虚拟的S对象的引用,用于驱动`detail::to_string_view`模板进行类型推断,从而判断是否可以继续编译。

-----2024年1月8日--------------

上面例子好长,刚刚又看到一个短的,比如,已知某个容器类型T,要求出该容器关联的迭代器的类型,下面的实现就很简洁。

cpp 复制代码
// An approximation of iterator_t for pre-C++20 systems.
template <typename T>
using iterator_t = decltype(std::begin(std::declval<T&>()));
template <typename T> 
using sentinel_t = decltype(std::end(std::declval<T&>()));
相关推荐
天天进步20152 分钟前
Java全栈项目 - 汽车维修服务管理平台
java·开发语言·汽车
CodeClimb11 分钟前
【华为OD-E卷-租车骑绿道 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
qq_4305839713 分钟前
QT笔记- QTreeView + QFileSystemModel 当前位置的保存与恢复 #选中 #保存当前索引
开发语言·笔记·qt
Crossoads20 分钟前
【汇编语言】外中断(一)—— 外中断的魔法:PC机键盘如何触发计算机响应
android·开发语言·数据库·深度学习·机器学习·计算机外设·汇编语言
Zik----22 分钟前
Anaconda搭建Python虚拟环境并在Pycharm中配置(小白也能懂)
开发语言·人工智能·python·机器学习·pycharm
易码智能24 分钟前
【RealTimeCallBack】- KRTS C++示例精讲(4)
c++·定时器·kithara·windows 实时套件·krts
凯子坚持 c29 分钟前
解锁仓颉编程语言的奥秘:枚举类型、模式匹配与类接口全解析
开发语言·华为·harmonyos
小王爱吃月亮糖29 分钟前
QT-QVariant类应用
开发语言·c++·笔记·qt·visual studio
计科土狗34 分钟前
基于c语言的union、字符串、格式化输入输出
c++
闻缺陷则喜何志丹35 分钟前
【C++动态规划】1458. 两个子序列的最大点积|1823
c++·算法·动态规划·力扣·最大·子序列·点积