推导具有两个以上参数的 std::function

2024-04-27

我想知道为什么std::function http://en.cppreference.com/w/cpp/utility/functional/function只知道有两个参数的函数。我已经编写了一些运行良好的代码,但存在许多限制。欢迎任何反馈。特别是,我怀疑我正在重新发明轮子。

我的代码已开启ideone http://ideone.com/vCegX我会参考它。

例如,我可以描述以下类型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”表示指针到指针到字符)

标准std::function不适用于具有两个以上参数的函数(请参阅我的代码的最后两行),但此代码可以(示例代码演示了三个参数函数)。也许我的设计应该在标准库中使用!我定义typedef tuple<Args...> args_as_tuple;存储所有参数,而不仅仅是前两个参数类型。

主要技巧是这个函数中的推导:

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

限制:

  • 它不适用于 lambda。这不会编译function_type_deducer([](){}).describe_me();
  • 它没有注意到之间存在微小的差异x and y, as y需要一个string&, where x需要一个string。 (std::function 也没有注意到这一点)

关于如何修复其中任何一个的任何想法?我重新发明了轮子吗?


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

如果function_type_deducer不是模板。 :) 非捕获 lambda(空[]) 可以隐式转换为函数指针。遗憾的是,某些模板参数推导没有考虑隐式转换。看这个问题 https://stackoverflow.com/q/8513050/500104获取更多信息(请注意,我的答案并不完全正确,如评论所示)。


它没有注意到 x 和 y 之间存在微小差异,因为 y 接受一个字符串&,而 x 接受一个字符串。

这不是功能的问题,是功能的问题typeid,如这个简单的测试代码所示:

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

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

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

Ideone 上的实例 http://ideone.com/YtDli。输出:

错误:“v”声明为引用但未初始化

一个简单的修复可能是使用标签调度:

#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>()代替typeid(NextArg).name().


我重新发明了轮子吗?

是的,有点像,不,你没有。升压功能 http://www.boost.org/doc/libs/1_48_0/doc/html/boost/functionN.html为所有参数提供 typedef(argN_type样式),以及静态常量arity对于其数量。但是,您通常无法轻松访问这些类型定义。您需要一种迂回的方式来避免意外访问不存在的内容。这tuple这个想法效果最好,但是可以用更好的方式编写。这是我曾经写过的内容的修改版本:

#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 上的实例 http://ideone.com/iySpC。输出:

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'
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

推导具有两个以上参数的 std::function 的相关文章

随机推荐