【问题标题】:Is it possible to use parameter pack to allow template function to accept equivalent types?是否可以使用参数包来允许模板函数接受等效类型?
【发布时间】:2014-01-28 05:46:40
【问题描述】:

这个问题与几年前 Georg Fritzsche 关于转换参数包 (Is it possible to transform the types in a parameter pack?) 的一个问题有关。最后,可以转换参数包中的各个类型,例如通过转换为相应的指针类型。

我想知道是否可以使用这种技术编写一个标准函数/函子和一组包装器函数(在一个模板之外),以便包装器可以获取等效类型的参数,然后调用标准函数做实际的工作。

使用 Johannes Schaub 的答案 - 点燃下面的原始示例。是否可以编写一个模板f,它可以采用int/int*,char/char* 的任意组合并调用通用函数f_std(int*,char*) 来完成这项工作。 (参数个数没有预先指定。)

--- 更新 --- 例如,给定int i; char c;,是否可以使用包转换编写调用程序,以便以下工作

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

下面列出了我到目前为止所尝试的内容(已更新以澄清。)。基本上,在调用采用指针类型的 std::function 之前,我尝试接受不一定是指针类型的列表并将它们转换为指针类型。但是代码无法编译。我不知道如何编写一个辅助函数来接受一个具有标准签名的函数,但接受一个其他的参数包。

提前致谢

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

【问题讨论】:

  • f 可以接受多少个参数?它是可变参数还是只有 2 个?您可以使用std::is_pointer 检查参数是否为指针,如果不是则添加指针。
  • @Gasim variadic,很有趣。
  • 我认为您要问的是不可能的,因为转换类型会阻止模板参数推导,从而阻止重载解析。但也许比我聪明的人会想出一个解决方法。

标签: c++ c++11


【解决方案1】:

如果我理解正确的话,这会从语法上回答你的问题:是的,有可能。

// 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)...);
}

不幸的是,调用f 将按值传递intchar 参数,因此复制它们。要解决此问题,请在 f 的定义中使用完美转发:

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

司机:

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

输出(示例;刚才在我的机器上运行):

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

【讨论】:

  • template&lt;class Arg, class... Args&gt; void f_std(Arg arg, Args... args) 在其上下文中看起来像是部分专业化,但它是一个重载。您不需要template&lt;class... Args&gt; void f_std(Args... args); 并将其专门用于template&lt;&gt; void f_std(),一个简单的void f_std() 重载就足够了。
  • 注意:std::ostream 有一个重载 operator&lt;&lt;(const void*) 以打印地址。
  • @Brian。 +1 感谢您的回答。如果我理解正确,您的答案适用于任何固定的f_std。我想我需要一个更通用的解决方案,其中f_std 也是call_ptr(f_std,i,&amp;c) 等更高功能的参数。这就是我对翻译类型感兴趣的原因。请查看更新。
  • @dyp:我认为他这样使用它是为了避免使用operator&lt;&lt;(const char*)
  • @Jarod42 那么,为什么不明确转换为void*std::cout &lt;&lt; static_cast&lt;void const*&gt;(arg) &lt;&lt; "\n";
【解决方案2】:

以下可能会有所帮助:

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

现在,使用它:

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

【讨论】:

  • +1。谢谢。这真的很接近我正在寻找的东西。我不知道我们可以在一个模板 语句中使用两个参数包。 (认为​​它需要嵌套类型。)另一方面,是否可以使其更通用并使用通用类型转换机制?我真的在寻找一种方法来进行类型转换,而不是添加/删除指针或对类型的引用。
  • @TingL:您可以使用class Functor 进行任何转换,例如struct make_pointer { template&lt;typename T&gt; operator() (T*) const; template&lt;typename T&gt; operator() (T&amp;) const; };,然后将其他模板参数添加到call_ptr。跨度>
【解决方案3】:

作为参考,以下是对我有用的方法,基于接受的答案@Jarod42 和类型转换“魔术”。稍微更通用一些,并增加了类型检查。原来类型转换只是一种模式扩展。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多