【问题标题】:How can I determine the return type of a functor which takes that type for a parameter?如何确定以该类型为参数的函子的返回类型?
【发布时间】:2020-03-17 16:05:17
【问题描述】:

假设我必须遵循以下功能:

template <typename Op, typename T> T foo(Op op) { }

Op假设有如下方法:

T Op::operator()(T&);

(这是一个简单的案例;实际上它可能有更多参数,但我知道它们的类型)。

现在,我想为T 参数设置一个默认值。问题是,为了虚假调用它(例如std::result_of&lt;Op::operator()(int&amp;)&gt;,我需要有参数的类型——这正是我所缺少的类型。

是否可以从Op 中确定T?例如,我可以调用:

foo( [](int& x){ return x++; } );

我对 C++11 解决方案感兴趣;如果您需要更高标准版本的功能,那也很有趣(尤其是解释为什么会这样)。

注意:如果Op 我自己有多个兼容的 operator()'s taking references to different types, I'm ok with having to specifyT`,当然 - 但是当我这样做时编译应该会通过。

【问题讨论】:

  • 应该foo(Op op) { }T foo(Op op) { }
  • 另外,你真的需要知道T吗?可以使用auto返回类型推导。
  • 既然函数以function作为参数,那么回调的参数从何而来?恕我直言,这在这里至关重要
  • 如果函数对象有多个不同返回类型的重载怎么办?
  • @NathanOliver:1. 是的。 2. 是的,因为没有它,我无法调用 Op。还有其他原因不在此示例中。

标签: c++ c++11 templates type-deduction return-type-deduction


【解决方案1】:

如果函数对象有正好一个签名,您可以通过decltype(&amp;T::operator())发现该签名并从中推断:

template<class T> struct X : X<decltype(&T::operator())> {}; // #1
template<class T> struct X<T(T&) const> { using type = T; }; // #2
template<class C, class M> struct X<M (C::*)> : X<M> {}; // #3
// add more specializations for mutable lambdas etc. - see example

template <typename Op, typename T = typename X<Op>::type>
T foo(Op op) { /* ... */ }

Example (C++11).


这是一系列类型转换,表示为部分模板特化。从匿名类型 &lt;lambda&gt; 的 lambda 中,我们在 #1 处提取其函数调用运算符类型,给出一个成员函数指针类型,例如 int (&lt;lambda&gt;::*)(int&amp;) const,它与 #3 匹配,允许我们丢弃类类型,给出 abominable函数类型int(int&amp;) const,匹配#2,允许我们提取参数和返回类型,暴露为X::type,通过继承对X的原始实例可见。将同一模板的部分特化用于不同类型的计算是一种code-golf 技巧,您可能希望在生产代码中避免它(并使用更具表现力的名称)。


另一方面,如果函数对象有多个签名(模板参数、默认参数、overloaded、手工制作的函数对象等),那么这将不起作用;你需要等待更强的反思形式才能进入语言。

【讨论】:

【解决方案2】:

模板函数的存在说明这个问题一般是不可能回答的:

struct post_increment
{
    template<typename T>
    T operator()(T& t) const
    {
        return t++;
    }
};

foo(post_increment{});
// or in C++14: foo([](auto& t) { return t++; });

函数fooT&amp; 映射到T 的某些类型集T 的结果本身就是多态的。在某些情况下,这甚至可以在 C++11 中表达:

template<typename Op>
struct foo_result_t
{
    template<typename T>
    operator T() const
    {
        T t {};
        op(t);
        return t;
    }

    Op op;
};

template <typename Op>
foo_result_t<Op> foo(Op op)
{
     return {op};
}

int i = foo(post_increment{}); // use it with an int

【讨论】:

  • 我不确定我是否遵循。首先,您的意思是问题是“不可能回答”,而不是“不可能”,对吧?此外,我们已经有了答案,尽管做了一个简化的假设。
  • “不可能”->“不可能回答”谢谢。是的,简化为函数指针或只有一个 operator() 的函数对象(大致相同)可以给出答案。我试图对答案做出贡献的是,即使在 C++11 中,改变问题(因为通常没有 T 可以推断)也可以在某些情况下提供有效/方便的解决方案。
【解决方案3】:

在只有一个签名的情况下的另一种解决方案。

另一个...嗯...几乎相同的ecatmur的解决方案,但基于模板函数,来推断类型,而不是模板类。

你所需要的只是一个声明函数两个声明的函数来推断类型(并返回与operator()相同的返回类型;你还可以开发另一个函数来返回,例如@ 987654323@,如果需要提取Args...类型)

template <typename T, typename R, typename ... Args>
R deducer (R(T::*)(Args...) const);

template <typename T, typename R, typename ... Args>
R deducer (R(T::*)(Args...));

和一个using(有点decltype() 谵妄)来提取R 类型

template <typename T>
using RetType = decltype(deducer(std::declval<decltype(&T::operator())>()));

以下是完整的 C++11 示例

#include <utility>

struct A
 {
   long operator() (int, char, short)
    { return 0l; }
 };

template <typename T, typename R, typename ... Args>
R deducer (R(T::*)(Args...) const);

template <typename T, typename R, typename ... Args>
R deducer (R(T::*)(Args...));

template <typename T>
using RetType = decltype(deducer(std::declval<decltype(&T::operator())>()));

int main ()
 {
   auto f = [](int& x) { return x++; };

   static_assert( std::is_same<RetType<A>, long>::value, "!" );
   static_assert( std::is_same<RetType<decltype(f)>, int>::value, "!" );
 }

如果您不想同时管理 volatile 运算符,则必须添加其他两个 deducer()

template <typename T, typename R, typename ... Args>
R deducer (R(T::*)(Args...) volatile const);

template <typename T, typename R, typename ... Args>
R deducer (R(T::*)(Args...) volatile);

【讨论】:

  • 我认为你的推断者doesn't work for lambdas
  • @einpoklum - 是的...我永远忘记了 constness 部分...要使其正常工作,您必须声明 mutable lambda(或者,要使其失败,您可以声明 @987654335 @operator()A)...我们需要第二个 deducer() 用于 const 运算符...几分钟...
  • @einpoklum - 已更正。好吧...部分更正:如果您还想管理volatile 方法(但有用的volatile 方法?)您必须添加另外两个deducer():一个用于volatile,一个用于volatile const。跨度>
猜你喜欢
  • 2012-11-25
  • 2020-12-30
  • 1970-01-01
  • 2021-03-18
  • 1970-01-01
  • 2011-12-18
相关资源
最近更新 更多