简体   繁体   English

为什么非捕获lambda不能默认构造,是否可以解决这个问题?

[英]Why can non-capturing lambdas not be default-constructed, and is it possible to work around this?

It is possible to use the type of a lambda as a template argument, like 可以使用lambda的类型作为模板参数,例如

template<typename InArg, typename Function>
class selfCompose {
  Function f;
 public:
  selfCompose(Function f): f(f) {}
  auto operator() (InArg x) -> decltype(f(f(x))) {
    return f(f(x));                              }
};

int main() {
  auto f = [](int x){return x*x;};
  std::cout << selfCompose<int, decltype(f)>(f)(4)  //  yields (4²)² = 256
            << std::endl;
  return 0;
}

However, this double use of f is kind of redundant. 然而, f这种双重用途是多余的。 We can omit passing the lambda's type as the template (casting it to a suitable std::function (at loss of polymorphism – but C++ lambdas aren't parametrically polymorphic anyway)), however I have an application where I'd much prefer not having to pass its value to the constructor (because I'd like to use my class's initialisations themselves as a template parameter, where a particular constructor signature is expected). 我们可以省略传递lambda的类型作为模板(将其转换为合适的std::function (失去多态性 - 但无论如何C ++ lambdas都不是参数化多态)),但是我有一个应用程序,我更不希望不必须将其传递给构造函数(因为我想将我的类的初始化本身用作模板参数,其中需要特定的构造函数签名)。 I'd like it to work like 我喜欢它的工作方式

template<class InArg, class Function>
class selfCompose {
  Function f;
 public:
  selfCompose() {}  // default constructor for f
  auto operator() (InArg x) -> decltype(f(f(x))) {
    return f(f(x));                              }
};

int main() {
  auto f = [](int x){return x*x;};
  std::cout << selfCompose<int, decltype(f)>()(4) << std::endl;
  return 0;
}

but this doesn't compile because lambdas have a deleted default constructor. 但这不能编译,因为lambdas有一个删除的默认构造函数。 Which is of course inevitable for capturing lambdas, but for simple ones like the one in my example this doesn't make much sense to me: they don't need to reference any local variables. 这对于捕获 lambdas当然是不可避免的,但是对于像我这个例子中那样简单的那个,这对我来说没有多大意义:它们不需要引用任何局部变量。

Is there some other way to get this functionality, or do I have to resort to defining the lambda old-fashionly as a named class? 是否有其他方法来获得此功能,或者我是否必须将lambda旧式定义为命名类?

struct myFun {
  auto operator() (int x) -> int {return x*x;}
};

(of course, the lambda functions I'd like to use aren't quite as simple as x → x² , so just selecting from a few standard function classes wouldn't be flexible enough) (当然,拉姆达功能我想使用是不是简单x → x² ,从几个标准功能类,所以只选择是不够灵活)

You can follow the example of functions like make_pair and make_shared : 您可以按照make_pairmake_shared等函数的示例进行操作:

template<typename InArg, typename Function>
selfCompose<InArg, Function> make_selfCompose(Function f)
{
  return selfCompose<InArg, decltype(f)>(f);
}

int main() {
  auto f = [](int x){return x*x;};
  std::cout << make_selfCompose<int>(f)(4)
            << std::endl;
  return 0;
}

If selfCompose is made polymorphic, there is no need to either explicitly pass the parameter type or to inspect the stored functor type. 如果使selfCompose多态,则无需显式传递参数类型或检查存储的selfCompose函数类型。 The latter means that it will also be able to deal with polymorphic functors. 后者意味着它也能够处理多态仿函数。

template<typename Functor>
struct self_compose_type {
    // Omitting encapsulation for brevity
    Functor functor;

    // It is possible to overload operator() to deal
    // with all cv and ref qualifiers combinations
    template<typename... T>
    auto operator()(T&&... t)
    // Don't use std::result_of for the return type
    -> decltype( std::declval<Functor&>()(std::declval<T>()...) )
    { return functor(std::forward<T>(t)...); }
};

template<typename Functor>
self_compose_type<Functor>
self_compose(Functor&& functor)
{ return { std::forward<Functor>(functor) }; }

// C++03 style:
template<typename Functor>
self_compose_type<typename std::decay<Functor>::type>
make_self_compose(Functor&& functor)
{ return { std::forward<Functor>(functor) }; }

It is not necessary to make the operator() variadic, you can make it accept exactly one argument if you so wish. 没有必要使operator()变量,如果你愿意,你可以让它接受一个参数。

Benjamin already posted a nice workaround for your case. 本杰明已经为你的案子发布了一个很好的解决方法。 You could use that. 你可以用它。

My solution is just an improved version of Benjamin's answer where you don't have to specify the lambda parameter type . 我的解决方案只是Benjamin的答案的改进版本,您不必指定lambda参数类型 So instead of writing this: 所以不要写这个:

make_selfCompose<int>(f)(4); //Benjamin's solution

you can write just this: 你可以这样写:

make_selfCompose(f)(4); //improved - absence `int` template argument.

and let make_selfCompose deduce the lambda parameter type itself. 并让make_selfCompose推导出lambda参数类型本身。

For this solution, lets write first function_traits class template as: 对于此解决方案,我们首先将function_traits类模板写为:

#include <tuple>

template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};

template <typename C, typename R, typename... A>
struct function_traits<R(C::*)(A...) const>
{
   template <size_t i>
   struct arg
   {
      typedef typename std::tuple_element<i, std::tuple<A...>>::type type;
   };
};

Then here is an improved version of make_selfCompose : 然后这是make_selfCompose的改进版本:

template<typename Fun> //<--- now it has one template parameter
selfCompose<typename function_traits<Fun>::template arg<0>::type, Fun>
make_selfCompose(Fun f)
{
  typedef typename function_traits<Fun>::template arg<0>::type InArg; //deduce it
  return selfCompose<InArg, decltype(f)>(f);
}

Here is test program: 这是测试程序:

int main() {
  auto f = [](int x){return x*x;};
  std::cout << make_selfCompose(f)(4)  //notice the relief here!
            << std::endl;
  return 0;
}

See an online demo 查看在线演示

Hope that helps. 希望有所帮助。 :-) :-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM