限制可变参数模板参数 [英] Restrict variadic template arguments

查看:117
本文介绍了限制可变参数模板参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们可以将可变参数模板参数限制为某种类型吗?即,实现这样的东西(当然不是真正的C ++):

Can we restrict variadic template arguments to a certain type? I.e., achieve something like this (not real C++ of course):

struct X {};

auto foo(X... args)

这里我的意图具有接受可变数量的 X 参数的函数。

Here my intention is to have a function which accepts a variable number of X parameters.

最接近的是: p>

The closest we have is this:

template <class... Args>
auto foo(Args... args)

但接受任何类型的参数。

but this accepts any type of parameter.

推荐答案

是的,可以。首先,你需要决定是否只接受类型,或者如果你想接受隐式转换类型。我在示例中使用 std :: is_convertible ,因为它更好地模仿非模板化参数的行为,例如。 long long 参数将接受 int 参数。如果由于什么原因你只需要显式传递这个类型,用 std:is_same 替换 std :: is_convertible (您可能需要添加 std :: remove_reference std :: remove_cv )。

Yes it is possible. First of all you need to decide if you want to accept only the type, or if you want to accept a implicitly convertible type. I use std::is_convertible in the examples because it better mimics the behavior of non-templated aparameters, e.g. a long long parameter will accept an int argument. If for what ever reason you need just that type to be passed explicitly, replace std::is_convertible with std:is_same (you might need to add std::remove_reference and std::remove_cv).

不幸的是,在 C ++ 缩小转换例如( long long int ,甚至 duble int )是隐式转换。而在古典设置中,当这些发生时你可以得到警告,你不会得到 std :: is_convertible 。至少不在电话。如果你做这样的赋值,你可能会在函数体中得到警告。

Unfortunately, in C++ narrowing conversion e.g. (long long to int and even duble to int) are implicit conversions. And while in a classical setup you can get warnings when those occur, you don't get that with std::is_convertible. At least not at the call. You might get the warnings in the body of the function if you make such an assignment. But with a little trick we can the error at call site with templates too.

因此,这里没有别的东西:

So without further ado here it goes:

测试装置:

struct X {};
struct Derived : X {};
struct Y { operator X() { return {}; }};
struct Z {};

foo_x : function that accepts X arguments

int main ()
{
   int i{};
   X x{};
   Derived d{};
   Y y{};
   Z z{};

   foo_x(x, x, y, d); // should work
   foo_y(x, x, y, d, z); // should not work due to unrelated z
};






概念



还没有,但很快。这将是最简单,清晰和优雅的解决方案。


Concepts

Not here yet, but soon. This will be the most simple, clear and elegant solution

template <class From, class To>
concept constexpr bool Convertible = std::is_convertible_v<From, To>;

template <Convertible<X>... Args>
auto foo_x(Args... args) {}

foo_x(x, x, y, d); // OK
foo_x(x, x, y, d, z); // error:



我们得到一个非常好的错误。特别是'Convertible< Z,X>'不满意是甜的。

error: cannot call function 'auto foo_x(Args ...) [with Args = {X, X, Y, Derived, Z}]'
     foo_x(x, x, y, d, z);
                        ^
note:   constraints not satisfied
auto foo_x(Args... args)
     ^~~~~
note: in the expansion of 'Convertible<Args, X>...'
note:     'Convertible<Z, X>' was not satisfied



处理缩小:



Dealing with narrowing:

template <class From, class To>
concept constexpr bool Convertible_no_narrow = requires(From f, To t) {
    t = {f};
};

template <Convertible_no_narrow<int>... Args>
auto foo_ni(Args... args) {}

foo_ni(24, 12); // OK
foo_ni(24, 12, 15.2);
// error:
// 'Convertible_no_narrow<double, int>' was not satisfied


$ b b

C ++ 17



我们利用非常好的折叠表达式

template <class... Args,
         class Enable = std::enable_if_t<(... && std::is_convertible_v<Args, X>)>>
auto foo_x(Args... args) {}

foo_x(x, x, y, d, z);    // OK
foo_x(x, x, y, d, z, d); // error

不幸的是,我们得到一个不太清楚的错误:

Unfortunately we get a less clear error:

template argument deduction/substitution failed:



缩小



我们可以避免变窄,但我们必须做一个trait is_convertible_no_narrowing p>

Narrowing

We can avoid narrowing, but we have to cook a trait is_convertible_no_narrowing (maybe name it differently:

template <class From, class To>
struct is_convertible_no_narrowing_impl {
  template <class F, class T,
            class Enable = decltype(std::declval<T &>() = {std::declval<F>()})>
  static auto test(F f, T t) -> std::true_type;
  static auto test(...) -> std::false_type;

  static constexpr bool value =
      decltype(test(std::declval<From>(), std::declval<To>()))::value;
};

template <class From, class To>
struct is_convertible_no_narrowing
    : std::integral_constant<
          bool, is_convertible_no_narrowing_impl<From, To>::value> {};



C + + 14



一个连接帮助器:

请注意,在 C ++ 17 中会有 std :: conjunction ,但它需要 std :: integral_constant 参数

C++14

We create a conjunction helper:
please note that in C++17 there will be a std::conjunction, but it will take std::integral_constant arguments

template <bool... B>
struct conjunction {};

template <bool Head, bool... Tail>
struct conjunction<Head, Tail...>
    : std::integral_constant<bool, Head && conjunction<Tail...>::value>{};

template <bool B>
struct conjunction<B> : std::integral_constant<bool, B> {};

现在我们可以使用我们的函数:

and now we can have our function:

template <class... Args,
          class Enable = std::enable_if_t<
              conjunction<std::is_convertible<Args, X>::value...>::value>>
auto foo_x(Args... args) {}


foo_x(x, x, y, d); // OK
foo_x(x, x, y, d, z); // Error



C ++ 11



对C ++ 14版本的一些小改动:

C++11

just minor tweaks to the C++14 version:

template <bool... B>
struct conjunction {};

template <bool Head, bool... Tail>
struct conjunction<Head, Tail...>
    : std::integral_constant<bool, Head && conjunction<Tail...>::value>{};

template <bool B>
struct conjunction<B> : std::integral_constant<bool, B> {};

template <class... Args,
          class Enable = typename std::enable_if<
              conjunction<std::is_convertible<Args, X>::value...>::value>::type>
auto foo_x(Args... args) -> void {}

foo_x(x, x, y, d); // OK
foo_x(x, x, y, d, z); // Error

这篇关于限制可变参数模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆