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

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

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

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

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

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

#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
 

#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` 等,用于操作和查询包装的常量。这些成员函数和成员类型可以用于进行类型推导、模板元编程和编译时条件判断等用途。

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

#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` 来获取枚举类型的底层类型:

#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限定符会让类型不同。

#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` 的派生类。

具体的使用示例如下:

// 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)。



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,要求出该容器关联的迭代器的类型,下面的实现就很简洁。

// 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&>()));

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值