我想要一个具有无限数量参数的函数,但我还要确保这些都是相同类型的指针。像这样:
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 ++实现吗?
答案 0 :(得分:7)
void myFunc(float value, std::initializer_list<MyClass*> il){
for(auto* p:il)
p->f(value);
}
不会发生堆/免费商店分配。
使用myFunc(3.14, {ptr1,ptr2,ptr3});
如果您真的讨厌{}
,可以使用不受限制的模板转发上述内容。在转发点,将进行类型检查。 SFINAE可以用于更早的类型检查。
template<class...MyClasses>
void myFunc2( float value, MyClasses*... ps) {
myFunc( value, {ps...} );
}
(可能名称更改)
或者,可以完成基于SFINAE的完整解决方案,直接调用p->f
,就像使用火箭筒来处理垃圾一样。当然,垃圾将会消失,但这仍然是一个坏主意。
初始化列表旨在有效地捆绑相同类型的参数。
现在,询问您MyClass*
请求的一个好问题是......为什么要关心?如果传入的参数与->f(3.14f)
兼容,为什么不调用->f(3.14f)
?这就是为什么实际问题是更好的问题而不是抽象的问题:问题的最佳解决方案因实际问题而异。
火箭筒解决方案如下所示。
首先,一个小模板元编程库:
// 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...>> {};
这一点以上的一切都是通用的元编程代码。你可以更直接地完成下一部分,但上面的通用元编程代码可以用于其他类似的问题,它确实可以使后来的东西更清晰&#34;。
// 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
更好的选择。
核心在这里:
test_cross<std::is_same, types<MyClass>, types<Ts...>>{}
这会为每对std::is_same<A,B>
评估<MyClass, Ts>
。
更容易实现的方法是使用bool...
一堆&&
并在std::is_same<MyClass, Ts>{}...
上加上{{1}}的模板。但我喜欢编写元编程库,并且简洁的n路交叉产品测试是一个更有趣的问题。
答案 1 :(得分:1)
您可以执行以下操作,然后您可以坚持使用现有语法..
#include <iostream>
#include <type_traits>
using namespace std;
struct V
{
int a;
int b;
};
// Little test function to ensure that it's the type we're expecting..
template <typename T>
int test(T*)
{
static_assert(is_same<T, V>::value, "Must only be V");
return 0;
}
template <typename ...T>
void foo(int a, T*... args)
{
// Use expansion to test all the parameters, this will generate
// an error if the wrong type is passed in
auto v = { (test(args), 0)... };
(void) v; // result discarded..
}
int main()
{
V a, b, c, d, e;
int f;
foo(10, &a, &b, &c, &d, &e, &f); // this will fail
foo(10, &a, &b, &c, &d, &e); // this will compile
}
基本上使用带有static_assert
的参数包来强制类型相同......