错误,无法在构造函数体中构造lambda的副本

时间:2017-09-26 01:50:48

标签: c++ templates lambda constructor

在下文中,接受一种初始化成员的方法,另一种方法是错误。我不明白:

template <typename T>
struct LambdaHolder
{
    LambdaHolder(T lambdaIn) { lambda = lambdaIn; } // This will not work
    LambdaHolder(T lambdaIn) : lambda(lambdaIn) { } // This will work fine
    T lambda;
};


int main()
{
    auto lambda = []() { return 0; };
    LambdaHolder<decltype(lambda) > foo(lambda); // This will work only if the constructor argument is assigned in the initialiser list

// On the other hand
    int(*lambdaPtr)() = lambda; // A function pointer
    LambdaHolder<decltype(lambdaPtr)> foo(lambdaPtr); // Will work either in initialiser list or constructor body

}

我不理解一些事情。我知道必须在成员初始化列表中初始化某些成员,例如引用和const成员。我不明白为什么它会让我在初始化列表中复制lambda而不是在构造函数体中复制lambda。错误是&#34;您无法构建lambda&#34;。

的副本

另外,我能够创建一个指向lambda的指针,并在初始化列表或构造函数体中初始化指针。现在,我知道lambdas和函数指针并不是完全相同的类型,但问题是我认为无捕获的lambda是作为自由函数实现的,这就是为什么你可以设置一个普通的函数指针,并且还认为当作为参数传递时,它肯定会衰减为指针。

基本上我想澄清一下,在构造函数体或初始化列表中初始化之间区别的区别是什么?

编辑:正如Whoan指出的那样,看起来lambda的默认构造函数在C ++ 14之前被删除了,而在C ++ 14之后我就不存在了。

2 个答案:

答案 0 :(得分:3)

这是因为lambdas不是Default Constructible

  

关闭类型不是DefaultConstructible。关闭类型有&#34;删除(直到C ++ 14)|不(因为C ++ 14)&#34;默认构造函数。

在这个构造函数中:

LambdaHolder(T lambdaIn) { lambda = lambdaIn; } // This will not work

... lambda需要在分配之前进行默认构建。

答案 1 :(得分:2)

首先,初始化和分配之间存在差异,尽管它们有时看起来很相似。

SomeClass obj1 = val;

是初始化。它会尝试将表达式val传递给SomeClass的构造函数,以便创建obj1。请注意,即使您为operator=定义了一个或多个SomeClass,上述代码也不会调用它们。

SomeClass obj2;
obj2 = val;

在此示例中,第一个语句仍需要初始化obj2以创建可以使用的有效对象,因此需要调用SomeClass的默认构造函数(由您定义或自动定义)编译器)。然后第二个语句使用obj2函数(也由您或编译器定义)分配给已初始化的对象operator=

每当调用构造函数时,必须在构造函数体启动之前初始化所有基类和成员。正文可能会尝试对成员做任何事情,因此他们需要在那时成为有效的初始化对象。

LambdaHolder(T lambdaIn) : lambda(lambdaIn) { }

对于任何类类型T,此构造函数使用参数lambda初始化名为lambdaIn的成员。由于它们具有相同的类型,这意味着调用复制构造函数。这个是完全有效的。

LambdaHolder(T lambdaIn) { lambda = lambdaIn; }

对于任何类类型T,由于成员lambda的mem-initializer缺失但成员必须在正文启动之前初始化,编译器会尝试调用{{1}的默认构造函数创建T。然后,正文lambda中的语句尝试调用lambda = lambdaIn;函数来从参数中分配成员。

是的,lambda的类型是类类型。 lambda本质上是一个快捷方式,用于创建一个类,其中最有用的成员是一个名为operator=的函数,因此您可以像函数一样调用它。

但lambda类型已删除默认构造函数和已删除的复制赋值运算符。这意味着您永远不会被允许调用它们,因此上述代码在两种不同的方式中无效。

顺便说一句,lambda表达式永远不会自动“衰减”成函数的指针,因为C风格的数组会衰变为指向第一个元素的指针,函数的名称会衰减成指向函数的指针。这种类型的衰减只发生在原始数组和函数类型中。相反,存在从没有捕获列表的lambda类型到函数指针的隐式转换。只有在您实际尝试初始化或从lambda分配特定匹配函数指针类型时才会使用隐式转换。