简体   繁体   English

是否可以使用参数包来允许模板函数接受等效类型?

[英]Is it possible to use parameter pack to allow template function to accept equivalent types?

This question is related to one on SO a couple of years ago by Georg Fritzsche about transforming a parameter pack ( Is it possible to transform the types in a parameter pack? ). 这个问题与Georg Fritzsche几年前关于转换参数包的问题有关( 是否可以转换参数包中的类型? )。 In the end, the individual types in the parameter pack can be transformed, eg by converting to corresponding pointer types. 最后,可以转换参数包中的各个类型,例如通过转换为相应的指针类型。

I am wondering if it is possible to use this technique to write one standard function/functor and a set of wrapper functions (out of one template), so that the wrappers can take parameters of equivalent types and then call the standard function to do actual work. 我想知道是否可以使用此技术来编写一个标准函数/函数和一组包装器函数(从一个模板中),以便包装器可以采用等效类型的参数,然后调用标准函数进行实际处理工作。

Using the answer by Johannes Schaub - litb the original example below. 使用Johannes Schaub的答案-引用以下原始示例。 Is it possible to write one template f , which can take any combinations of int/int*,char/char* and call a common function f_std(int*,char*) to do the work. 是否可以编写一个模板f ,它可以采用int/int*,char/char*任意组合并调用通用函数f_std(int*,char*)来完成工作。 (The number of parameters is not pre-specified.) (参数数量未预先指定。)

--- Update --- For example, given int i; char c; ---更新---例如,给定int i; char c; int i; char c; , is it possible to write a caller using pack transformation such that the following works ,是否可以使用pack转换编写调用方,从而实现以下工作

  call_ptr(f_std,i,c);
  call_ptr(f_std,&i,c);
  call_ptr(f_std,i,&c);

What I've tried so far is listed below (updated to clarify.). 下面列出了我到目前为止已经尝试过的内容(已更新以澄清。)。 Basically, I tried to accept an list of not necessarily pointer types and convert them to pointer types, before making call to a std::function that takes pointer types. 基本上,在调用带有指针类型的std :: function之前,我尝试接受不一定是指针类型的列表并将其转换为指针类型。 But the code does not compile. 但是代码无法编译。 I don't know how to write a helper function to accept one function with a standard signature, but accept a parameter pack of something else. 我不知道如何编写一个辅助函数来接受带有标准签名的一个函数,但是却接受其他参数包。

Thanks in advance 提前致谢

#include <type_traits>
#include <functional>
using namespace std;

template<class... Args> struct X {};
template<class T> struct make_pointer     { typedef T* type; };
template<class T> struct make_pointer<T*> { typedef T* type; };

template<template<typename...> class List, 
         template<typename> class Mod, 
         typename ...Args>
struct magic {
    typedef List<typename Mod<Args>::type...> type;
};

/////////////////
// trying to convert parameter pack to pointers
template<class T> T* make_ptr(T x) { return &x; }
template<class T> T* make_ptr(T* x) { return x; }  
template <typename Value, typename ...Args>
class ByPtrFunc
{
public:
  typedef typename magic<X, make_pointer, Args...>::type PArgs;
  Value operator()(Args... args) { return f(make_ptr(args)...);  }

private:
  std::function<Value (PArgs...)> _ptr_func;

}; //ByPtrFunc

//helper function to make call
template<typename A, typename ...Args>
static A call_ptr(std::function<A (Args...)> f, Args... args) {
  return ByPtrFunc<A, Args...>{f}(args ...);
}

int main() {
  typedef magic<X, make_pointer, int*, char>::type A;
  typedef X<int*, char*> B;
  static_assert(is_same<A, B>::value, ":(");

  int i=0; char c='c';
  function<int (int* pa,char* pb)> f_std = [](int* pa,char* pb)->int {return *pa + * pb;};
  f_std(&i,&c);
  //////////////////
  //Is the following possible.
  call_ptr(f_std,i,c);
  call_ptr(f_std,&i,c);
  call_ptr(f_std,i,&c);

  return 0;
}

This answers your question syntax-wise, if I've understood it correctly: yes, it's possible. 如果我正确理解,这将以语法方式回答您的问题:是的,这是可能的。

// given int or char lvalue, returns its address
template<class T>
T* transform(T& t) {
    return &t;
}

// given int* or char*, simply returns the value itself
template<class T>
T* transform(T* t) {
    return t;
}

// prints out the address corresponding to each of its arguments    
void f_std() {
}

template<class Arg, class... Args>
void f_std(Arg arg, Args... args) {
    std::cout << (void*)arg << std::endl;
    f_std(args...);
}

// converts int to int*, char to char*, then calls f_std
template<class... Args>
void f(Args... args) {
    f_std(transform(args)...);
}

Unfortunately, calling f will pass int and char arguments by value, and hence copy them. 不幸的是,调用f将按值传递intchar参数,并因此它们复制 To fix this, use perfect forwarding in the definition of f : 要解决此问题,请在f的定义中使用完美转发:

template<class... Args>
void f(Args&&... args) {
    f_std(transform(std::forward<Args>(args))...);
}

Driver: 司机:

int main() {
    int x = 1;
    char c = 'a';
    cout << (void*)&x << endl;
    cout << (void*)&c << endl;
    f(x, &x, c, &c);
}

Output (example; ran it on my machine just now): 输出(示例;仅在我的机器上运行):

0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebc
0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebb

Following may help: 以下内容可能会有所帮助:

template <typename T> T* make_pointer(T& t) { return &t; }
template <typename T> T* make_pointer(T* t) { return t; }

template <typename Ret, typename... Args, typename ...Ts>
Ret call_ptr(std::function<Ret (Args*...)> f, Ts&&...args)
{
    static_assert(sizeof...(Args) == sizeof...(Ts), "Bad parameters");
    f(make_pointer(std::forward<Ts>(args))...);
}

Now, use it: 现在,使用它:

void f_std(int*, char*) { /* Your code */ }

int main(int argc, char *argv[])
{
    int i;
    char c;

    std::function<void (int*, char*)> f1 = f_std;

    call_ptr(f1, i, c);
    call_ptr(f1, i, &c);
    call_ptr(f1, &i, c);
    call_ptr(f1, &i, &c);

    return 0;
}

For reference, below is what worked for me, based on the accepted answer @Jarod42 and the type transformation "magic". 作为参考,以下是对我有用的方法,基于公认的答案@ Jarod42和类型转换“ magic”。 slightly more general and with added type checking. 更加通用,并增加了类型检查。 Turns out type-transformation is simply a pattern expansion. 事实证明类型转换只是一种模式扩展。

#include <type_traits>
#include <functional>
#include <iostream>
using namespace std;

/////////////////
// convert parameter pack to pointers
//types
template<class T> struct make_ptr_t     { typedef T* type; };
template<class T> struct make_ptr_t<T*> { typedef T* type; };
//values
template<class T> T* make_ptr(T& x) { return &x; }
template<class T> T* make_ptr(T* x) { return x; }

/////////////////////////////////////
// (optional) only for type checking
template<class... Args> struct X {};
template<template<typename...> class List, 
         template<typename> class Mod, 
         typename ...Args>
struct magic {
    typedef List<typename Mod<Args>::type...> type;
};

//helper function to make call
template<typename A, typename ...PArgs, typename ...Args>
static A call_ptr(std::function<A (PArgs...)> f, Args... args) {
  static_assert(is_same<X<PArgs...>,typename magic<X, make_ptr_t, Args...>::type>::value, "Bad parameters for f in call_ptr()"); //type checking
  return f(make_ptr(args)...);
}

int main() {
  int i=0; char c='c'; string s="c";
  function<int (int* pa,char* pb)> f_std = [](int* pa,char* pb)->int {return *pa + * pb;};
  f_std(&i,&c);

  cout << call_ptr(f_std,i,c) << endl;
  cout << call_ptr(f_std,&i,c) << endl;
  cout << call_ptr(f_std,i,&c) << endl;
  //cout << call_ptr(f_std,i,s) << endl; //complains about bad parameters.
  return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM