【问题标题】:"Manual" signature overload resolution“手动”签名过载解决方案
【发布时间】:2013-02-20 05:33:33
【问题描述】:

我想创建一个类似std::function 的对象,它可以处理存储多个重载。

语法类似于:my_function< int(double, int), double(double, double), char(int, int) >

或者,更明确地说:

template<typename... Ts>
struct type_list {};

template<typename... Signatures >
struct my_function {
  std::tuple< std::function<Signatures>... > m_functions;
  typedef type_list< Signatures... > sig_list;
  template<typename... Args>
  typename pick_overload_signature< sig_list, type_list<Args...> >::return_value
    operator()( Args&&... args )
  {
    return get<pick_overload_signature< sig_list, type_list<Args...> >::index>(m_functions)(std::forward<Args>(args)...);
  }
};

我的问题:我应该怎么写pick_overload_signatures

这是我所做的工作:

我的倾向是针对给定的一组参数编写函数签名的偏序,然后对函数签名的类型列表进行排序,然后获取最好的(可能在编译时断言最好的一个是独特)。为了实现这一点,我必须在函数签名上有一个可靠的偏序(相对于传入的一组参数)......

13.3.3.1 告诉我如何确定是否存在有效转换。我可以为此作弊,使用编译器为我进行转换,并使用 SFINAE 检测它是否发生在传入的给定参数和“重载”之一的签名中。

13.3.3.2 告诉我如何排序这些转换。在这里,我必须检测转换序列是用户定义的还是标准序列。我不知道如何区分这两者。

也许我可以使用特征类来检测用户定义的转换序列的存在。检查是否存在 &amp;S::operator D()&amp;D::D(S const&amp;)&amp;D::D(S)&amp;D::D(S&amp;&amp;) 或类似的东西。

has_user_defined_conversion&lt;S,D&gt;::valuehas_standard_conversion&lt;S,D&gt;::value 等?

这种方法会奏效吗?有人已经这样做了,还是有人已经完成了其中的一部分?

Result of Answers

#include <type_traits>
#include <cstddef>
#include <utility>
#include <functional>
#include <tuple>
#include <string>

// Packaged list of types:
template<typename... Ts>
struct type_list {
   template<template<typename...>class target>
   struct apply {
      typedef target<Ts...> type;
   };
   template<typename T>
   struct append {
      typedef type_list< Ts..., T > type;
   };
   template<typename T>
   struct prepend {
      typedef type_list< T, Ts... > type;
   };
};
template<template<typename>class mapper, typename list>
struct map_types {
   typedef type_list<> type;
};
template<template<typename>class mapper, typename T0, typename... Ts>
struct map_types<mapper, type_list<T0, Ts...>> {
   typedef typename map_types<mapper, type_list<Ts...>>::type tail;
   typedef typename tail::template prepend< typename mapper<T0>::type >::type type;
};
template<template<typename>class mapper, typename list>
using MapTypes = typename map_types<mapper, list>::type;
template<template<typename>class temp>
struct apply_template_to {
   template<typename T>
   struct action {
      typedef temp<T> type;
   };
};
template<template<typename> class temp, typename list>
struct apply_to_each:map_types< apply_template_to<temp>::template action, list > {};
template<template<typename> class temp, typename list>
using ApplyToEach = typename apply_to_each<temp, list>::type;

template<std::size_t n, typename list>
struct nth_type {};
template<std::size_t n, typename first, typename... elements>
struct nth_type<n, type_list<first, elements...>>:nth_type<n-1, type_list<elements...>>
{};
template<typename first, typename... elements>
struct nth_type<0, type_list<first, elements...>>
{
   typedef first type;
};
template<std::size_t n, typename list>
using NthType = typename nth_type<n, list>::type;

// func data
template<typename R, typename... Args>
struct unpacked_func {
   typedef R result_type;
   typedef type_list<Args...> args_type;
   typedef unpacked_func< R, Args... > unpacked_type;
   template<template<typename>class target>
   struct apply {
      typedef target<R(Args...)> type;
   };
};

namespace unpack_details {
   // Extracting basic function properties:
   template<typename Func>
   struct unpack_func {};
   template<typename R, typename... Args>
   struct unpack_func< R(Args...) > {
      typedef unpacked_func< R, Args... > type;
   };
   template<typename R, typename... Args>
   struct unpack_func< unpacked_func<R, Args...> >:
      unpack_func< R(Args...) >
   {};
}

template<typename Func>
using FuncUnpack = typename unpack_details::unpack_func<Func>::type;

template<typename Func>
struct func_props:func_props<FuncUnpack<Func>> {};
template<typename R, typename... Args>
struct func_props<unpacked_func<R, Args...>>:
   unpacked_func<R, Args...>
{};

template<typename Func>
using FuncResult = typename func_props<Func>::result_type;
template<typename Func>
using FuncArgs = typename func_props<Func>::args_type;

template<typename Func>
struct make_func_ptr:make_func_ptr<FuncUnpack<Func>> {};

template<typename R, typename... Args>
struct make_func_ptr< unpacked_func< R, Args... > > {
   typedef R(*type)(Args...);
};
template<typename Func>
using MakeFuncPtr = typename make_func_ptr<Func>::type;

// Marking a type up with an index:
template<typename R, std::size_t i>
struct indexed_type {
   typedef R type;
   enum { value = i };
};

// Sequences of size_t:
template<std::size_t... s>
struct seq {};
template<std::size_t min, std::size_t max, std::size_t... s>
struct make_seq: make_seq< min, max-1, max-1, s...> {};
template<std::size_t min, std::size_t... s>
struct make_seq< min, min, s...> {
     typedef seq<s...> type; 
};
template<std::size_t max, std::size_t min=0>
using MakeSeq = typename make_seq<max, min>::type;

namespace overload_details {
   template<std::size_t n, typename... Overloads>
   struct indexed_linear_signatures {};

   template<typename Overload>
   struct signature_generator {};
   template<typename R, typename... Args>
   struct signature_generator<unpacked_func<R, Args...>> {
      R operator()(Args...); // no impl
   };


   template<typename Func, std::size_t i>
   struct indexed_retval {};

   template<typename R, typename... Args, std::size_t i>
   struct indexed_retval< unpacked_func<R, Args...>, i > {
      typedef unpacked_func<indexed_type<R,i>, Args...> type;
   };

   template<typename Func, std::size_t i>
   using IndexRetval = typename indexed_retval<Func,i>::type;

   void test1() {
      typedef overload_details::IndexRetval< FuncUnpack<void()>, 0 > indexed;
      indexed::apply<std::function>::type test = []()->indexed_type<void,0> {return indexed_type<void,0>();};
   }

   template<std::size_t n, typename Overload, typename... Overloads>
   struct indexed_linear_signatures<n, Overload, Overloads...>:
      signature_generator<IndexRetval<FuncUnpack<Overload>,n>>,
      indexed_linear_signatures<n+1, Overloads...>
   {};

   template<typename T>
   struct extract_index {};
   template<typename T, std::size_t i>
   struct extract_index<indexed_type<T,i>> {
      enum {value = i};
   };

   template<typename T>
   using Decay = typename std::decay<T>::type;

   template<typename indexed_overloads, typename... Args>
   struct get_overload_index {
      enum{ value = extract_index< Decay<decltype( std::declval<indexed_overloads>()(std::declval<Args>()...) )> >::value };
   };

   template<typename Overloads, typename Args>
   struct get_overload {};
   template<typename... Overloads, typename... Args>
   struct get_overload<type_list<Overloads...>, type_list<Args...>> {
      typedef indexed_linear_signatures<0, Overloads...> sig_index;
      enum { index = get_overload_index< sig_index, Args... >::value };
      typedef FuncUnpack< NthType<index, type_list<Overloads...> > > unpacked_sig;
   };

   template<typename Overloads, typename Args>
   using GetOverloadSig = typename get_overload< Overloads, Args >::unpacked_sig;
}

template<typename Overloads, typename Arguments>
struct pick_overload_signature {
   enum{ index = overload_details::get_overload<Overloads, Arguments>::index };
   typedef overload_details::GetOverloadSig<Overloads, Arguments> unpacked_sig;
};
#include <iostream>
void test1() {
   typedef type_list< void(int), void(double) > overloads;
   typedef type_list< int > args;
   typedef pick_overload_signature< overloads, args > result;
   std::cout << result::index << " should be 0\n";
   typedef type_list< double > args2;
   typedef pick_overload_signature< overloads, args2 > result2;
   std::cout << result2::index << " should be 1\n";

//    ;
   typedef ApplyToEach< std::function, overloads >::apply< std::tuple >::type functions;
   typedef std::tuple< std::function<void(int)>, std::function<void(double)> > functions0;
   std::cout << std::is_same<functions, functions0>() << " should be true\n";

   functions funcs{
      [](int) { std::cout << "int!" << "\n"; },
      [](double) { std::cout << "double!" << "\n"; }
   };
   std::get<result::index>(funcs)(0);
}

template< typename... Signatures >
struct my_function {
   typedef type_list<Signatures...> signatures;
   typedef std::tuple< std::function<Signatures>... > func_tuple;
   func_tuple functions;
   template<typename... Funcs>
   explicit my_function(Funcs&&... funcs):
      functions( std::forward<Funcs>(funcs)... )
   {}

   template<typename... Args>
   auto
   operator()(Args&&... args) const ->
      typename overload_details::GetOverloadSig< signatures, type_list<Args...> >::result_type
   {
      return std::get<
         pick_overload_signature< signatures, type_list<Args...> >::index
      >(functions)(std::forward<Args>(args)...);
   }
   // copy/assign boilerplate
   template<typename... OtherSignatures>
   my_function( my_function<OtherSignatures...> const& o ):
      functions( o.functions )
   {}
   template<typename... OtherSignatures>
   my_function( my_function<OtherSignatures...> && o ):
      functions( std::move(o.functions) )
   {}
   template<typename... OtherSignatures>
   my_function& operator=( my_function<OtherSignatures...> const& o )
   {
      functions = o.functions;
      return *this;
   }
   template<typename... OtherSignatures>
   my_function& operator=( my_function<OtherSignatures...> && o ) {
      functions = std::move(o.functions);
      return *this;
   }
};

struct printer {
   template<typename T>
   void operator()( T const& t ) {
      std::cout << t << "\n";
   }
};

void print(int x) {
   std::cout << "int is " << x << "\n";
}
void print(std::string s) {
   std::cout << "string is " << s << "\n";
}
void test2() {
   my_function< void(int), void(std::string) > funcs{
      [](int x){ std::cout << "int is " << x << "\n";},
      [](std::string s){ std::cout << "string is " << s << "\n";}
   };
   std::cout << "test2\n";
   funcs("hello");
   funcs(0);
   my_function< void(int), void(std::string) > funcs2{
      printer(), printer()
   };
   funcs2("hello");
   funcs2(12.7);
   // doesn't work:
   /*
   my_function< void(int), void(std::string) > funcs3{
      print,
      print
   };
   */
}
void test3() {

}
int main() {
   test1();
   test2();
   test3();
}

尚未完成,但可以使用。

谢谢大家!

【问题讨论】:

  • 非常有趣的问题,关于如何回答,我有一些想法,但我必须在开始工作时尝试解决这个问题。
  • 您是否希望能够存储同一函数的不同重载,或者根据参数调用不同的函数?您能否举例说明您打算如何使用您的课程模板?
  • @AndyProwl 我对(目前)根据参数调用不同的函数感到满意,“好像”它们参与重载决议。然后,您可以将该功能多次重复为“不同功能”。消除这种重复可能很好(并且可以通过一些宏+完美的转发模板 lambda tomfoolery 来实现),但不是必需的。
  • 用于实验:ideone.com/NDpkB5。请忽略丑陋的实现:)
  • @johannesschaub 您是否使用成对重载测试来订购签名?光滑!

标签: c++ templates c++11 overload-resolution


【解决方案1】:

我确定这是可行的,但也许你会对这个感到满意https://gist.github.com/dabrahams/3779345

template<class...Fs> struct overloaded;

template<class F1, class...Fs>
struct overloaded<F1, Fs...> : F1, overloaded<Fs...>::type
{
typedef overloaded type;

overloaded(F1 head, Fs...tail)
: F1(head),
overloaded<Fs...>::type(tail...)
{}
using F1::operator();
using overloaded<Fs...>::type::operator();
};

template<class F>
struct overloaded<F> : F
{
typedef F type;
using F::operator();
};

template<class...Fs>
typename overloaded<Fs...>::type overload(Fs...x)
{ return overloaded<Fs...>(x...); }

auto f = overload(
[](int x) { return x+1; },
[](char const* y) { return y + 1; },
[](int* y) { return y; });

【讨论】:

  • 这是否会像函数重载那样做同样的“调度”选择?
  • @Yakk:它必须这样做,因为它有效地为 operator() 创建了一个带有替代签名的重载集。
  • 啊,是的,operator() 不是垂直相关的,所以没有一个优先于另一个。不错的技巧——将其调整为std::function 风格的界面应该很容易!唯一的缺点是因为我们直接使用编译器的重载解析机制,我们无法使用它(或更改它)。我还可以通过在未评估的上下文中使用上述内容来获得正确的重载索引,从而实现完美转发。
【解决方案2】:

我认为您可以使用类似这些特征的东西...但是如果您想完全按照标准进行重载解析 - 您需要更多代码 http://en.cppreference.com/w/cpp/language/implicit_cast

#include <type_traits>

template<typename T, typename D>
struct is_constructible
{
   template<typename C, typename F>
   static auto test(C*) -> decltype(C(std::declval<F>()), std::true_type());
   template<typename, typename>
   static std::false_type test(...);
   static const bool value = std::is_class<T>::value && 
      std::is_same<std::true_type, decltype(test<T, D>(0))>::value;
};

template<typename T, typename D>
struct has_conversion_operator
{
   static std::true_type test(D d);
   template<typename C, typename F>
   static auto test(C* c) -> decltype(test(*c));
   template<typename, typename>
   static std::false_type test(...);

   static const bool value = std::is_class<T>::value && 
      !is_constructible<T, D>::value && 
      std::is_same<std::true_type, decltype(test<T, D>(0))>::value;
};

template<typename T, typename D>
struct is_standard_convertible : 
   std::integral_constant<bool, !has_conversion_operator<T, D>::value && 
   !is_constructible<T, D>::value &&
   std::is_convertible<T, D>::value>
{
};

template<typename T, typename D>
struct is_user_convertible :
   std::integral_constant<bool, has_conversion_operator<T, D>::value || 
   is_constructible<T, D>::value>
{
};

并实现你想要的: 首先检查,签名是standard_convertible 如果不是,请检查签名是否为 user_convertible。

【讨论】:

    猜你喜欢
    • 2023-02-21
    • 2014-08-30
    • 1970-01-01
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多