簡體   English   中英

C ++函數具有無限參數但具有相同(固定)類型

[英]C++ function with unlimited parameters but with the same (fixed) type

我想要一個具有無限數量參數的函數,但我也想確保那些都是相同類型的指針。 像這樣的東西:

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 ++實現嗎?

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

這一點之上的一切都是通用的元編程代碼。 你可以更直接地完成下一部分,但上面的通用元編程代碼可以用於其他類似的問題,它確實使后面的東西“更清晰”。

// 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...>>{}

這會為每對<MyClass, Ts>計算std::is_same<A,B> <MyClass, Ts>

一個更容易實現的方法是使用一堆bool...並在它們上面執行&& ,與std::is_same<MyClass, Ts>{}... 但我喜歡編寫元編程庫,並且簡潔地進行n路交叉產品測試是一個更有趣的問題。

實例

基於python的堆棧溢出答案的交叉產品

您可以執行以下操作,然后您可以堅持使用現有語法..

#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的參數包來強制類型相同...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM