【问题标题】:Is it possible to define a lambda in C++ with default generic argument?是否可以使用默认泛型参数在 C++ 中定义 lambda?
【发布时间】:2018-06-08 14:03:45
【问题描述】:

是否可以在 C++ 中使用默认泛型参数定义 lambda?

int main(){
    auto lambda = [](auto i = 0){return i;};
    std::cout<<lambda()<<"\n"; // this does not compile
    std::cout<<lambda(4)<<"\n"; // this does compile
    auto lambda2 = [](int i = 0){return i;};
    std::cout<<lambda2()<<"\n"; // this is also OK
}

我想知道是否可以重现类似这个函子的东西,为什么不可以

struct Lambda{
    template<typename T=int>
    auto operator()(T i=1){ return i;}
};

【问题讨论】:

  • lambda 的问题是它会产生类似struct Lambda{ template&lt;typename T&gt; auto operator()(T i=1) const{ return i;} }; 的东西,不能用作Lambda{}()
  • 在 C++20 中使用模板 lambda 是可能的
  • @Jarod42 但是为什么在你的例子中推论不起作用?
  • @HolyBlackCat: "非推导上下文是: [...] — 用于函数形参的形参类型中的模板形参,该形参具有正在使用的默认实参正在进行参数推导的调用。”.

标签: c++ c++11 lambda c++14 auto


【解决方案1】:

你可以用包装器做到这一点:

template<class F, class DefaultArg>
struct DefaultArgWrapper
{
    F f;
    DefaultArg default_arg;

    template<class... Args>
    decltype(auto) operator()(Args&&... args) {
        return f(std::forward<Args>(args)...);
    }

    decltype(auto) operator()() {
        return f(default_arg);
    }
};

template<class F, class DefaultArg>
DefaultArgWrapper<F, DefaultArg> with_default_arg(F&& f, DefaultArg arg) {
    return {std::move(f), std::move(arg)};
}

int main(){
    auto lambda = with_default_arg([](auto i){return i;}, 0);
    std::cout<<lambda()<<"\n"; 
    std::cout<<lambda(4)<<"\n";
}

另一种 C++17 解决方案:

template<class... F>
struct ComposeF : F... {
    template<class... F2>
    ComposeF(F2&&... fs)
        : F(std::forward<F2>(fs))...
    {}
    using F::operator()...;
};

template<class... F>
ComposeF<std::decay_t<F>...> composef(F&&... fs) {
    return {std::forward<F>(fs)...};
}

int main() {
    auto lambda = [](auto i) { return i; };
    auto f = composef(lambda, [&lambda](int i = 0) { return lambda(i); });
    std::cout << f() << '\n';
    std::cout << f(1) << '\n';
}

虽然有点次优,因为涉及到lambda 的两个副本:ComposeF 中的一个副本,另一个是堆栈上的原始lambda。如果lambda 是可变的,那将是一个问题。

【讨论】:

  • ... 需要注意的是,与“true”默认参数不同,初始化 arg 的表达式仅在创建仿函数时计算一次。这也可能有一些可能意想不到的生命周期交互,例如当DefaultArgstd::initializer_list 的实例时,如bool my_is_sorted(const std::vector&lt;int&gt; &amp;v); ... with_default_arg(my_is_sorted, {1,2,3}) // UB on call
  • @ArneVogel 您的反例无法编译,因为它无法从{1,2,3} 推断出DefaultArg。尽管关于差异,您是完全正确的,它在某处可能很重要,但我还没有看到生产代码。大多数情况下,默认参数是普通类型。
猜你喜欢
  • 2012-12-24
  • 2010-12-16
  • 2020-03-11
  • 1970-01-01
  • 2019-10-15
  • 1970-01-01
  • 1970-01-01
  • 2018-08-29
  • 1970-01-01
相关资源
最近更新 更多