推导std ::函数有两个以上的args [英] Deducing std::function with more than two args

查看:120
本文介绍了推导std ::函数有两个以上的args的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不知道为什么 std :: function 只知道两个 - 报价功能。我写了一些工作很好的代码,但有一些限制。任何反馈欢迎。特别是,我怀疑我是重新发明的轮子。

I wonder why std::function is only aware of two-argument functions. I've written some code which is working well, but there are a number of limitations. Any feedback welcome. In particular, I suspect I'm reinventing the wheel.

我的代码位于 ideone 上,我将参考

My code is on ideone and I will refer to it.

例如,我可以用以下形式描述 main 的类型:

For example, I can describe the type of main with:

function_type_deducer(main).describe_me();
// Output: I return i and I take 2 arguments.  They are of type:  i PPc

(其中'i'表示'int'和'PPc'指针到指针到字符)

(where 'i' means 'int' and 'PPc' means pointer-to-pointer-to-char)

标准std ::函数不能用于具有多于两个参数的函数(请参阅我的代码的最后两行),但是这段代码(示例代码演示了三个arg函数)。也许我的设计应该在标准库中使用!我定义 typedef tuple< Args ...>

Standard std::function doesn't work with functions with more than two args (see the last two lines of my code), but this code does (the sample code demonstrates three-arg functions). Maybe my design should be used in the standard library instead! I define typedef tuple<Args...> args_as_tuple; to store all args, not just the first two argument types.

主要技巧是在这个函数中的扣除:

The main trick is the deduction in this function:

template<class T, class... Args>
auto function_type_deducer(T(Args...)) -> Function__<T, Args...> {
        return Function__<T, Args...> {};
}

限制:


  • 它不适用于lambdas。这不会编译 function_type_deducer([](){})describe_me();

  • x y 之间的小区别,因为 y 需要一个 string& ,其中 x 需要一个 string 。 (std :: function也不会注意到这一点)

  • It doesn't work with lambdas. This won't compile function_type_deducer([](){}).describe_me();
  • It doesn't notice that there is a small difference between x and y, as y takes a string&, where x takes a string. (std::function doesn't notice this either)

有关如何解决这些问题的任何想法?

Any ideas on how to fix either of these? Have I reinvented the wheel?

推荐答案


这不会编译 function_type_deducer ([](){})。describe_me();

$ c> function_type_deducer 不是模板。 :)非捕获lambdas(空 [] )可以隐式转换为函数指针。可悲的是,对于某些模板参数扣除,不考虑隐式转换。有关详细信息,请参阅此问题(注意,我的回答并不完全正确,因为评论指出)。

It would work if function_type_deducer wasn't a template. :) Non-capturing lambdas (empty []) are implicitly convertible to function pointers. Sadly, implicit conversions aren't taken into consideration for some template argument deduction. See this question for more information (note that my answer isn't completely correct as the comments indicate).


它没有注意到x和y之间有一个小的区别,因为y需要一个字符串& amp ;,其中x接受一个字符串。

It doesn't notice that there is a small difference between x and y, as y takes a string&, where x takes a string.

这不是函数的问题,这是 ,因为这个简单的测试代码显示:

That's not a problem with the function, that's a problem with typeid, as this simple testcode shows:

template<class T>
void x(void(T)){
    T v;
    (void)v;
}

void f1(int){}
void f2(int&){}

int main(){
    x(f1);
    x(f2);
}

Ideone上的实例。输出:


错误:'v'声明为引用,但未初始化

error: ‘v’ declared as reference but not initialized

一个简单的修复可能是使用标签分派:

A simple fix might be using tag dispatching:

#include <type_traits> // is_reference
#include <iostream>
#include <typeinfo>

template<class T>
void print_name(std::true_type){
  std::cout << "reference to " << typeid(T).name();
}

template<class T>
void print_name(std::false_type){
  std::cout << typeid(T).name();
}

template<class T>
void print_name(){
  print_name(typename std::is_reference<T>::type());
}

并调用 print_name< NextArg> code>而不是 typeid(NextArg).name()


我重新发明了轮子吗?

Have I reinvented the wheel?

't。 Boost.Function 为所有参数提供typedef( argN_type style),以及其数量的静态常量 arity 。然而,你不能容易地访问那些typedefs一般。你需要一个迂回的方式,不要偶然访问不存在的。 tuple 的想法效果最好,但它可以写得更好一点。这是我以前写的一个修改版本:

Yes, kind of and no, you didn't. Boost.Function provides typedefs for all arguments (argN_type style), aswell as the static constant arity for the number thereof. However, you can't easily access those typedefs generically. You would need a roundabout way to not accidentially access non-existant ones. The tuple idea works best, however it can be written in a nicer way. Here's a modified version of something I once wrote:

#include <tuple>
#include <type_traits>
#include <iostream>
#include <typeinfo>

namespace detail{

template<class T>
std::ostream& print_name(std::ostream& os);

template<class T>
std::ostream& print_pointer(std::ostream& os, std::true_type){
  typedef typename std::remove_pointer<T>:: type np_type;
  os << "pointer to ";
  return print_name<np_type>(os);
}

template<class T>
std::ostream& print_pointer(std::ostream& os, std::false_type){
  return os << typeid(T).name();
}

template<class T>
std::ostream& print_name(std::ostream& os, std::true_type){
  return os << "reference to " << typeid(T).name();
}

template<class T>
std::ostream& print_name(std::ostream& os, std::false_type){
  return print_pointer<T>(os, typename std::is_pointer<T>::type());
}

template<class T>
std::ostream& print_name(std::ostream& os){
  return print_name<T>(os, typename std::is_reference<T>::type());
}

// to workaround partial function specialization
template<unsigned> struct int2type{};

template<class Tuple, unsigned I>
std::ostream& print_types(std::ostream& os, int2type<I>){
  typedef typename std::tuple_element<I,Tuple>::type type;

  print_types<Tuple>(os, int2type<I-1>()); // left-folding
  os << ", ";
  return print_name<type>(os);
}

template<class Tuple>
std::ostream& print_types(std::ostream& os, int2type<0>){
  typedef typename std::tuple_element<0,Tuple>::type type;
  return print_name<type>(os);
}

} // detail::

template<class R, class... Args>
struct function_info{
  typedef R result_type;
  typedef std::tuple<Args...> argument_tuple;
  static unsigned const arity = sizeof...(Args);

  void describe_me(std::ostream& os = std::cout) const{
    using namespace detail;
    os << "I return '"; print_name<result_type>(os);
    os << "' and I take '" << arity << "' arguments. They are: \n\t'";
    print_types<argument_tuple>(os, int2type<arity-1>()) << "'\n";
  }
};

Ideone的Live示例。输出:

main:   I return 'i' and I take '2' arguments. They are: 
        'i, pointer to pointer to c'
x:      I return 'Ss' and I take '3' arguments. They are: 
        'i, Ss, c'
y:      I return 'Ss' and I take '3' arguments. They are: 
       'i, reference to Ss, c'

这篇关于推导std ::函数有两个以上的args的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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