是否可以允许一个std :: function类型接受具有不同签名的lambdas

tin*_*lyx 13 c++ c++11 std-function

我有一个更高阶的函数map,类似于STL for_each,并将一个std::function对象映射vector到一些东西上.

template<class T, class U>
vector<U> map(function<U (T)> f, vector<T> xs) {
  vector<U> ret;
  for (auto &x: xs)
    ret.push_back(f(x));
  return ret;
}
Run Code Online (Sandbox Code Playgroud)

现在,我想有这样的高阶功能拍摄的类型两个对象function<int (const vector<T>&)>function<int (vector<T>)>,如所附小例子.

问题是,function<int (const vector<T>&)>并且 function<int (vector<T>)>似乎可以相互转换(请参阅headhead2),但map不会采用const引用版本function<int (const vector<int>&)>(请参阅参考资料Q1).

可以map通过显式转换(Q2)来接受const引用版本,但这很麻烦.

我想知道,一般来说,是否可以编写一个deref删除const引用的函数function<int (const vector<T>&)>并返回一个function<int (vector<T>)>

(如果可以,那么我将不必为const refs编写两个相同的重载/ map实现).

谢谢.

#include <vector>
#include <functional>
using namespace std;

template<class T, class U>
vector<U> map(function<U (T)> f, vector<T> xs) {
  vector<U> ret;
  for (auto &x: xs)
    ret.push_back(f(x));
  return ret;
}

int main() {
  vector<vector<int>> m;
  function<int (const vector<int>&)> head  =  [](const vector<int>& a) {return a[0];};
  function<int (const vector<int>&)> head1 =  [](vector<int> a) {return a[0];}; //conversion OK
  function<int (vector<int>)> head2 =  [](const vector<int>& a) {return a[0];}; //conversion OK
  map(head2,m); //OK

  map(head,m); //Q1: problem line, implicit conversion NOT OK
  map(function<int (vector<int>)>(head),m); //Q2: explicit conversion OK
  map(deref(head),m); //Q3: ??How-to, deref takes a std::function f and returns a function with const ref removed from its signature

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

---编辑---

我对deref类似函数或元函数特别感兴趣,它可以从std::function对象的类型签名中删除const ref ,这样我至少可以Q2自动执行.

我知道,正如@Brian和@Manu正确指出的那样,使用std::function指定类型并不是常规的,但我想知道我上面提到的内容是否可行.就个人而言,std::function考虑到Func<T1, T2, T3, ...,Tn, Tresult>在C#中如何使用泛型函数类型,我认为代码更清晰.如果类型擦除的成本是可以容忍的话.

我完全同意c ++可以推断返回类型,并在类型错误时给出错误消息.也许这只是一个品味问题,我更愿意在编写功能签名时拼写出来.

Man*_*726 21

我明白你使用的原因std::function:你必须知道转换的返回类型才能创建向量,对吧?

但考虑一种完全不同的方法.给定元函数,std::result_of您可以计算函数调用的结果类型,所以只需写:

template<typename F , typename CONTAINER , typename T = typename std::result_of<F(typename CONTAINER::value_type)>::type>
std::vector<T> map( F f , CONTAINER&& container )
{
    std::vector<T> result;

    for( auto& e : container )
        result.emplace_back( f( e ) );

    return result;
}
Run Code Online (Sandbox Code Playgroud)

好处:

  • 不滥用std::function:始终考虑做什么std::function(即类型擦除),不要将它用作通用功能类型.

  • 依靠duck类型而不是类型上的耦合:不要担心,如果出现错误,它也不会编译.

  • 适用于任何标准库容器,因为我们使用value_type特征提取元素类型,而不是std::vector直接使用.

  • 代码更清晰,更有效,因为减少了std::function使用量.

关于" 它可以编写一个接受多个签名的lambda的函数吗? "这个问题.

使用std::function你可以在几行中写一些类似于Boost.OverloadedFunction的东西:

template<typename F , typename... Fs>
struct overloaded_function : public std_function<F> , public std_function<Fs>...
{
    overloaded_function( F&& f , Fs&&... fs ) :
        std_function<F>{ f },
        std_function<Fs>{ fs }...
    {}
};
Run Code Online (Sandbox Code Playgroud)

std_function给定函数类型的元函数在哪里F返回std::function具有签名的实例F.我将其作为游戏/挑战留给读者.

就这样.使用类似make的功能改进它:

template<typename F , typename... Fs>
overloaded_function<F,Fs...> make_overloaded_function( F&& f , Fs&&... fs )
{
    return { std::forward<F>( f ) , std::forward<Fs>( fs )... };
}
Run Code Online (Sandbox Code Playgroud)

你准备好了:

auto f = make_overloaded_function( [](){ return 1; } ,
                                   [](int,int){ return 2; } ,
                                   [](const char*){ return 3; } );

f();        //Returns 1
f(1,2);     //Returns 2
f("hello"); //Returns 3
Run Code Online (Sandbox Code Playgroud)

编辑:" 谢谢.但是,我真正想要的是一个元函数,它采用可调用的签名,并从签名中删除const引号. "

好吧,让我试试:std::decay元函数应用在通过值传递argumments到给定类型时完成的衰减.这包括删除cv限定符,删除引用等.所以像你这样的元函数可能是一个采用函数签名类型并将衰减应用于其所有argumments的东西:

template<typename F>
struct function_decay;

template<typename R typename... ARGS>
struct function_decay<R(ARGS...)>
{
    using type = R(typename std::decay<ARGS>::type...);
};
Run Code Online (Sandbox Code Playgroud)

那应该做的工作.

我之所以写这篇文章是因为你在评论中明确要求它,但我强烈建议你使用我最初展示的替代方案,因为它与你的方式相比有许多优点.
也就是说,我希望这个答案有助于解决您的问题.


Bri*_*ian 4

惯用的解决方案是简单地允许map采用任意类似函数的类型,

template<class T, class F>
auto map(F f, vector<T> xs) -> vector<typename result_of<F(T)>::type> {
  vector<typename result_of<F(T)>::type> ret;
  for (auto &x: xs)
    ret.push_back(f(x));
  return ret;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的主要问题是,如果F不可使用 类型的参数调用T,或者返回一些奇怪的内容(例如 ),您会收到令人困惑的错误消息void

(第二个问题是第一个参数不能map是重载函数;编译器不能简单地选择采用类型参数的重载T。)

(您可能还需要考虑衰减 的返回类型f。)