用于根据条件选择编译时类型的可变参数模板

Variadic template for compile-time type selection based on condition

本文关键字:变参 参数 类型 条件 选择 编译 用于      更新时间:2023-10-16

为了更好地理解可变参数模板,我给自己设定了一个任务,即根据给定的条件(在<type_traits>中定义的条件中选择(编写编译时类型选择器,例如std::is_signedstd::is_floating_point等(。选择器应在指定为模板参数的类型中选择满足条件的第一个类型。

举个例子:

template<template<typename> class Cond, typename... T> 
struct first_if_any {
// some code here
};
first_if_any<std::is_signed, unsigned, long, int>::type a; // long
first_if_any<std::is_unsigned, short, unsigned long, unsigned>::type b; // unsigned long
first_if_any<std::is_floating_point, int, float, double>::type c; // float

这些是我希望我的选择器具有的功能:

  1. 如果没有类型满足条件,请选择第一个类型
  2. 如果未指定类型,则打印用户友好的编译错误

因此:

first_if_any<std::is_unsigned, long, int>::type a; // long
first_if_any<std::is_arithmetic>::type b; // ERROR

这是我想到的(请参阅此处的工作示例(:

template<template<typename> class Cond, typename... T> 
struct first_if_any {
using type = void;
static constexpr bool found = false;
};
template<template<typename> class Cond, typename First, typename... T> 
struct first_if_any<Cond, First, T...> {
using type = typename std::conditional<Cond<First>::value || !first_if_any<Cond, T...>::found, First, typename first_if_any<Cond, T...>::type>::type;
static constexpr bool found = Cond<First>::value || first_if_any<Cond, T...>::found;
};

这将按预期选择类型,并满足要求 1。现在回答我的问题:

  • 我如何满足要求 2,即如果有人尝试使用选择器而不向其传递类型,则生成用户友好的编译错误?
  • 有没有更好的方法可以做到这一点(仅使用标准库功能(?

奖金问题,如果有人愿意详细说明:

  • 这符合模板元编程的条件吗?

要获得漂亮的错误消息,您必须将声明更改为

template<template<typename> class Cond, typename T, typename... Ts>
struct first_if_any;

因此first_if_any<Cond>会产生类似于以下内容的消息:

错误:类模板"first_if_any"的模板参数太少

然后,您当前实现的问题在于您使用了您想要禁止的内容,我的意思是first_if_any<Cond>(使用各种first_if_any<Cond, T...>,其中T...可以为空(。

您可以使用中间类,它更容易处理默认类型:

template<template<typename> class Cond, typename Default, typename... Ts>
struct first_if_any_or_default;
template<template<typename> class Cond, typename Default>
struct first_if_any_or_default<Cond, Default>
{
using type = Default;
static constexpr bool found = false;
};
template<template<typename> class Cond, typename Default, typename T, typename... Ts>
struct first_if_any_or_default<Cond, Default, T, Ts...>
{
private:
using next = first_if_any_or_default<Cond, Default, Ts...>;
public:
using type = typename std::conditional<Cond<T>::value,
T,
typename next::type>::type;
static constexpr bool found = Cond<T>::value || next::found;
};
template<template<typename> class Cond, typename First, typename... Ts> 
struct first_if_any {
private:
using helper = first_if_any_or_default<Cond, First, First, Ts...>;
public:
using type = typename helper::type;
static constexpr bool found = helper::found;
};