gpt4 book ai didi

c++ - 限制可变参数模板参数

转载 作者:IT老高 更新时间:2023-10-28 14:02:04 29 4
gpt4 key购买 nike

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

struct X {};

auto foo(X... args)

我的意图是有一个函数接受可变数量的 X 参数。

我们最接近的是:

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

但这接受任何类型的参数。

最佳答案

是的,这是可能的。首先,您需要决定是只接受类型,还是接受隐式可转换类型。我在示例中使用 std::is_convertible 是因为它更好地模仿了非模板化参数的行为,例如long long 参数将接受 int 参数。如果出于某种原因您只需要接受该类型,请将 std::is_convertible 替换为 std:is_same (您可能需要添加 std::remove_referencestd::remove_cv)。

不幸的是,在 C++ 缩小转换例如(long longint 甚至 doubleint)都是隐式转换。虽然在经典设置中,您可以在发生这些情况时收到警告,但使用 std::is_convertible 不会得到警告。至少不是在通话中。如果您进行这样的分配,您可能会在函数主体中收到警告。但是通过一个小技巧,我们也可以在调用站点使用模板得到错误。

所以这里不用多说:


测试台:

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
};

C++20 概念

还没有,但很快。 在 gcc 主干(2020 年 3 月)中可用。这是最简单、清晰、优雅和安全的解决方案:

#include <concepts>

auto foo(std::convertible_to<X> auto ... args) {}

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

我们得到一个非常好的错误。特别是

constraints not satisfied

很甜。

处理缩小:

我在库中没有找到概念,所以我们需要创建一个:

template <class From, class To>
concept ConvertibleNoNarrowing = std::convertible_to<From, To>
&& requires(void (*foo)(To), From f) {
foo({f});
};

auto foo_ni(ConvertibleNoNarrowing<int> auto ... args) {}

foo_ni(24, 12); // OK
foo_ni(24, (short)12); // OK
foo_ni(24, (long)12); // error
foo_ni(24, 12, 15.2); // error

C++17

我们使用非常好的fold expression :

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

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

template argument deduction/substitution failed: [...]

收窄

我们可以避免变窄,但我们必须制作一个特征 is_convertible_no_narrowing(可能名称不同):

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 参数

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 = 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 版本的细微调整:

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

关于c++ - 限制可变参数模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39659127/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com