存储lambda作为成员混淆

时间:2014-12-29 20:37:07

标签: c++ c++11 visual-studio-2013 lambda

我正在阅读Scott Meyer的 Effective Modern C ++ ,并点击了他建议使用lambda代替std::functionstd::bind的项目。我理解他的论点和他对std::function的弊端的主张,我同意他的看法。

截至今天,我决定切换到用于存储lambda s(不需要增变器)的模板。我确实理解每个lambda的类型只有编译器知道,甚至两个相同的lambda将具有不同的类型,那么为什么以下代码编译并且工作正常呢?

template<typename LambdaT>
class CaptureLambda
{
public:
    CaptureLambda(const LambdaT& fn)
        : mActionFn(fn) // initialize mActionFn to a user supplied lambda
    {}

private:
    LambdaT    mActionFn{ []{} }; // initialize mActionFn to an empty lambda
};

我的困惑在于,mActionFn是如何默认启动到成员声明中具有不同类型的空lambda,但是类的构造函数很乐意接受其中的另一种类型的lambda参数呢?他们cast彼此能够相处吗?如果是,为什么以下使编译器感到悲伤?

// Class stuff...

template<typename T>
void resetActionFn(const T& newFn) { // setter member
    mActionFn = newFn;
}

// Class stuff...

1 个答案:

答案 0 :(得分:4)

只有在构造函数未指定其他初始化程序时才会使用非静态数据成员初始化程序。

鉴于

struct S {
  int i = 3;
  S() : i(4) { }
};

你没有得到一个默认构造函数,首先将i初始化为3,然后将其重新初始化为4,你只需要一个初始化{{1}的构造函数} i

你的班级也一样。您没有任何初始化4的构造函数,因此初始化程序不会被使用。

现在,正如Piotr S.在评论中指出的那样,一般来说,intialiser仍然必须在语义上有效,但是您的数据成员具有依赖类型,因此无法在模板定义时检查有效性,并且初始化者从不由于它未被使用而被实例化,因此错误在实例化时也未被检测到。一个类似的简单例子是

mActionFn
即使template <typename T> struct S { T x = 3; S() : x(0) { } }; int main() { S<void*>(); } 3类型字段的无效初始化,GCC也会默默接受

。 clang拒绝了它。该标准尚不清楚模板的实例化是否导致任何未使用的NSDMI的实例化,并且一些编译器仅在需要时实例化它们。 There is agreement that they should be instantiated only as needed, but there are some problems with that approach, which is why not all compilers implement that yet.