C ++函数具有无限参数但具有相同(固定)类型 [英] C++ function with unlimited parameters but with the same (fixed) type

查看:208
本文介绍了C ++函数具有无限参数但具有相同(固定)类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个函数有无限数量的参数,但我还要确保那些都是相同类型的指针。类似这样:

I want to have a function with unlimited number of parameters but I want also make sure that those are all pointers of the same type. Something like this:

void myFunc(float value, MyClass* ....)
{
    // take all pointers of type MyClass and call function `f` like this: a->(value);
    // store pointer in a vector like: vector.push_back(a);
}

我可以在C ++中实现吗?

Can I achieve this in C++?

推荐答案

void myFunc(float value, std::initializer_list<MyClass*> il){
  for(auto* p:il)
    p->f(value);
}

不会发生堆/空闲存储分配。

no heap/free store allocation will occur.

使用 myFunc(3.14,{ptr1,ptr2,ptr3});

如果你真的讨厌 {} ,你可以转到上面的无限制模板。在转发点将有类型检查。

If you really hate {} you can forward to the above with an unrestricted template. At the point of forwarding there will be type checking. SFINAE can be used to type check earlier.

template<class...MyClasses>
void myFunc2( float value, MyClasses*... ps) {
  myFunc( value, {ps...} );
}

(可能会更改名称)

或者,可以使用完全基于SFINAE的解决方案,直接调用 p> f ,这就像使用bazooka来处理垃圾。当然,垃圾将消失,但它仍然是一个坏主意。

Alternatively, full SFINAE based solutions can be done, with direct calling of p->f, bit that is like using a bazooka to deal with litter. Sure, the litter will be gone, but it was still a bad idea.

初始化列表设计用于有效捆绑相同类型的参数。

initializer lists are designed for efficient bundling of parameters of identical type.

现在,关于你的 MyClass * 请求的一个好问题是...你为什么关心?如果传入的参数与 - > f(3.14f)兼容,为什么不调用 - > f(3.14f) code>?这是为什么实际问题是更好的问题,而不是抽象问题:对问题的最佳解决方案因实际问题而异。

Now, a good question to ask about your MyClass* request is ... why do you care? If the passed in parameters are compatible with ->f(3.14f), why not just call ->f(3.14f)? This is why practical problems are better problems to ask about, rather than abstract ones: the best solution to a problem varies with practical issues.

bazooka解决方案如下所示。

The bazooka solution looks like the follows.

首先,一个小模板元编程库:

First, a small template metaprogramming library:

// Better name for the type:
template<bool b>
using bool_t = std::integral_constant<bool, b>;
// bundle of types:
template<class...>struct types{using type=types;};

// takes a test, and a types<?...> of things to test against it:
template<template<class...>class Z, class args>
struct test_lists:std::true_type{};
template<template<class...>class Z, class...Ts, class...Us>
struct test_lists<Z,types<types<Ts...>,Us...>>:bool_t<
  Z<Ts...>{} && test_lists<Z, types<Us...>>{}
>{};

// takes 0 or more types<?...> and concatenates them:
template<class...types>
struct concat_types;
template<class...types>
using concat_types_t=typename concat_types<types...>::type;
template<>
struct concat_types<>:types<>{};
template<class...Ts>
struct concat_types<types<Ts...>>:types<Ts...>{};
template<class...T0s,class...T1s, class...more>
struct concat_types<types<T0s...>,types<T1s...>,more...>:
  concat_types_t< types<T0s...,T1s...>, more... >
{};

// takes a template Z and and arg, and produces a template
// equal to Z<Arg, ?...>:
template<template<class...>class Z, class Arg>
struct bind_1st {
    template<class...Ts>
    using apply=Z<Arg,Ts...>;
};

// takes a template Z and a types<?...> and produces
// types< Z<?>... >:
template<template<class...>class Z, class types>
struct map;
template<template<class...>class Z, class types>
using map_t=typename map<Z,types>::type;
template<template<class...>class Z, class...Ts>
struct map<Z,types<Ts...>>:types<Z<Ts>...>{};

// builds a cross product of zero or more types<?...>:
template<class...types0>
struct cross_types;
template<class...types>
using cross_types_t=typename cross_types<types...>::type;

// valid degenerate cases:
template<class...Ts>
struct cross_types<types<>,Ts...>:types<>{};
template<>
struct cross_types<>:types<types<>>{};

// meat of cross_types:
template<class T0, class...T0s, class...Us>
struct cross_types<types<T0,T0s...>, Us...>:
  concat_types_t<
    map_t< bind_1st< concat_types_t, types<T0> >::template apply, cross_types_t<Us...> >,
    cross_types_t< types<T0s...>, Us... >
  >
{};

// takes a test Z, and a sequence of types<?...> args
// tests the cross product of the contents of the args:
template<template<class...>class Z, class...Args>
struct test_cross : test_lists<Z, cross_types_t<Args...>> {};

这一点以上的一切都是通用元编程代码。你可以更直接地做下一部分,但上面的泛型元编程代码可以用于其他类似的问题,它确实使以后的东西更清楚。

everything above this point is generic metaprogramming code. You can do the next part more directly, but the generic metaprogramming code above can be used in other similar problems, and it does make the later stuff "clearer".

// a toy MyClass type to test against:
struct MyClass {
    void f(float x){
        std::cout << x << '\n';
    }
};

// Simple SFINAE test that the types passed in are exactly
// pointers to MyClass:
template<class...Ts>
std::enable_if_t<
  test_cross<std::is_same, types<MyClass>, types<Ts...>>{}
>
myFunc( float x, Ts*... p ) {
  using discard=int[];
  (void)discard{0,((
    p->f(x)
  ),void(),0)...};
}

请注意 std :: is_base_of 可能是比 is_same 更好的选择。

note that std::is_base_of might be a better choice than is_same.

核心在这里:

  test_cross<std::is_same, types<MyClass>, types<Ts...>>{}

对于每对 std :: is_same code>< MyClass,Ts>

this evaluates std::is_same<A,B> for every pair of <MyClass, Ts>.

一个更容易的方法是一个模板, bool ... ,并在它们上加上&& 以及 std: :is_same< MyClass,Ts> {} ... 。但我喜欢编写元编程库,简洁地进行n路交叉产品测试是一个更有趣的问题。

A far easier way to do it would be a template that took a bunch of bool... and did an && on them, together with std::is_same<MyClass, Ts>{}.... But I like writing metaprogramming libraries, and doing n-way cross product tests with brevity is a more interesting problem.

实例

跨产品基于这个堆栈溢出回答在python

这篇关于C ++函数具有无限参数但具有相同(固定)类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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