【问题标题】:Storing lambdas as members confusion将 lambdas 存储为成员混淆
【发布时间】:2015-02-26 00:56:42
【问题描述】:

我正在阅读 Scott Meyer 的 Effective Modern C++ 并点击了他建议使用 lambdas 代替 std::functionstd::bind 的项目。我理解他的论点和他关于std::function 缺点的说法,我同意他的看法。

从今天开始,我决定改用模板来存储lambdas(不需要修改器)。我确实明白每个lambda 的类型只有编译器知道,甚至两个相同的lambdas 也会有不同的类型,那么下面的代码怎么编译和工作得很好?

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...

【问题讨论】:

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


    【解决方案1】:

    只有在构造函数没有指定不同的初始化器时才会使用非静态数据成员初始化器。

    给定

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

    你不会得到一个默认构造函数,它首先将i初始化为3,然后将其重新初始化为4,你只是得到一个将i初始化为4的构造函数。

    你的班级也是如此。你没有任何构造函数初始化mActionFn,所以初始化器永远不会被使用。

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

    template <typename T>
    struct S {
      T x = 3;
      S() : x(0) { }
    };
    int main() {
      S<void*>();
    }
    

    即使3void* 类型字段的无效初始化程序,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.

    【讨论】:

    • 这是否意味着用于 nsdmi 的表达式可能无效?我怀疑。这就像你试图拥有std::string s = 1;,说它永远不会被使用,因为你在你拥有的每个ctor中初始化s
    • @PiotrS。在模板类中,位会根据需要进行实例化。例如,给定template &lt;typename T&gt; struct S { void f() { T(); } };,你可以拥有S&lt;void&gt; 类型的对象,你只是不能调用它的f 成员函数。确切地说,什么时候应该实例化哪些位仍然是一个悬而未决的问题,并且其中存在一些编译器变体。
    • 因为类模板的非虚拟成员函数在上下文需要时被实例化。我认为这不适用于数据成员。很高兴为此提供标准参考。
    • @PiotrS。找到它(不在标准中),将其添加到我的答案中。
    猜你喜欢
    • 1970-01-01
    • 2016-04-28
    • 2013-09-12
    • 2014-11-20
    • 1970-01-01
    • 2015-07-01
    • 2017-06-12
    • 2013-03-06
    • 1970-01-01
    相关资源
    最近更新 更多