模板函数与带有自动参数的命名 lambda

Posted

技术标签:

【中文标题】模板函数与带有自动参数的命名 lambda【英文标题】:Template functions versus named lambdas with auto parameters 【发布时间】:2013-06-15 19:48:01 【问题描述】:

两者有什么区别

template <typename T> void func( T t )  /* ... */ 

以及使用带有自动参数的 lambdas 的 C++14 替代方案?

auto func = []( auto t )  /* ... */ 

应该首选哪一个?

【问题讨论】:

你不能将auto t作为参数 在 C++14 中这是合法的。 很高兴知道这将在 c++14 中发生。 是的,它真的很有用。 Overloads 想和你谈谈。 【参考方案1】:

第一个是函数模板。它可以是专门的和重载的。它可以通过 ADL 找到。当你想获取地址时,你必须要么显式地给它模板参数,要么在编译器可以推断它们的上下文中进行。

第二个,假设它出现在命名空间范围内,是一个带有模板化函数调用运算符的全局对象。它不能被专门化或重载(全局变量与函数冲突,它们不会重载它们)。 ADL 找不到它(ADL 只能找到函数和函数模板)。如果在其上使用地址运算符,则得到对象的地址,这没什么用。如果编译器可以推导出参数,则可以将对象本身转换为函数指针;你不能明确地提供它们。

你可以使用任何你想要的;请注意任何一种选择的优缺点。我会推荐第一个。第二种的唯一优点是简洁,我希望在不久的将来我们也能获得函数模板的简洁语法。

auto func(auto t)  ... 

【讨论】:

【参考方案2】:

不同的是,第一个是函数模板,你必须在使用它之前定义它;一旦定义在那里,任何人都可以使用它。所以它是一段可重用的代码,并且永远保留在那里。

另一方面,Lambda 很方便:您可以在需要时定义它。如果 lambda 在函数内部定义为本地对象,则只有该函数可以使用它并将其传递给其他函数。它仍然是可重用的,但不如函数模板。然而,在命名空间级别定义的 lambdas 与函数模板一样可重用,因为任何人都可以使用它。因此,在命名空间级别定义它时,它与函数模板没有太大区别。专家可能会发现一些极端情况。一种这样的情况是,您可以专门化函数模板:

//specialization : possible only in case of template!
template<> void func(MyClass obj)  /* ... */ 

你不能用 lambdas 做到这一点!

【讨论】:

你可以通过以下方式进行特化 typedef decltype([](auto t) /* ... / ) Lambda;结构 : Lambda 使用 Lambda::operator(); void operator()( MyClass obj ) / ... */ func; 如果你真的想的话。 @RalphTandetzky:这不是专业化,不是模板的工作方式! 它实际上在做同样的事情。我也可以在那里插入 template,但这并没有太大的区别,是吗?【参考方案3】:

N3337,[expr.prim.lambda]/3:

lambda 表达式的类型(也是 闭包对象)是一个唯一的、未命名的非联合类类型——称为 闭包类型——其属性如下所述。此类类型 不是一个集合(8.5.1)。闭包类型在 包含的最小块作用域、类作用域或命名空间作用域 对应的 lambda 表达式。

这种闭包类型将保持一个类。但是它的重载函数调用操作符将是一个操作符函数模板,允许不同的特化。此外,与函数模板不同,您可以将闭包对象隐式转换为函数指针。它真的很方便,不是吗? 引用 N3559,它看起来像这样:

对于通用 lambda L:

int(*fp)(int, char) = [](auto a, auto b)return a+b;;

闭包类型是

struct/*anonymous*/

    template<class A,class B>
    auto operator()(A a,B b) const
    
        return a+b;
    

    private:
        template<class A,class B>
        static auto __invoke(A a,B b)
        
            return a+b;
        

        template<class A,class B,class R>
        using fptr_t = R(*)(A,B);

    public:

        template<class A,class B,class R>
        operator fptr_t<R,A,B>() const
        
            return &__invoke<A,B>; // Fixed that manually, they forgot to qualify the template name
        
 L;

int(*fp)(int,char) = L;

(会进行通常的模板参数推导)

【讨论】:

这种到函数指针的转换只有在 lambda 没有捕获任何东西的情况下才有效,对吧? 有了上面的定义,没错(调用者是静态的)。但我确信这很容易解决。编辑:嗯,这确实可以很容易地修复 - 您将为捕获的引用和对象使用代理对象,例如overgive __invoke 指向它的指针。 我查看了 N3559。静态函数和转换运算符仅在自动生成的 lambda 类中,如果没有捕获到数据。

以上是关于模板函数与带有自动参数的命名 lambda的主要内容,如果未能解决你的问题,请参考以下文章

带有模板参数的 Lambda 函数,而不是函数参数

将 lambda 作为模板参数传递给函数指针函数模板化

使用带有 lambda 函数指针的模板

Lambda 函数调用函数模板参数的静态函数失败

传递给模板函数时,lambda自动衰减到函数指针

C ++ lambda作为函数的模板参数不起作用