lambda 表达式的惰性实例化

lazy instantiation for lambda expression

我想在 lambda 表达式中访问 foo::func(),但此时 class foo 已声明但未定义。 lambda 表达式有什么办法吗?

如果我用等效的函数对象替换 lambda 表达式,那么我就可以做到。

这是等效的代码:

单独的声明和定义方法

struct foo; // forward declaration

struct lambda {
    void operator()(foo& f); // `lambda` only has declaration of `operator()`.
};

struct bar {
    void memfun(foo& f) {
        // Call `lambda` function object with the reference of incomplete `foo`.
        lambda()(f);
    }
};

struct foo { // Define foo
    void func() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

// Define `lambda::operator()` after definition of `foo`.
inline void lambda::operator()(foo& f) {
    f.func();
}

int main() {
    foo f;
    bar b;
    b.memfun(f);
}

运行 演示:https://wandbox.org/permlink/12xV6655DZXZxLqF

它可以在 g++ 和 clang++ 上编译。

我的目标是 Lambda 表达式方法

我试图消除 struct lambda

代码如下:

struct foo; // forward declaration

struct bar {
    void memfun(foo& f) {
        // Write explicit return type seems to instanciate 
        // lambda body lazily on g++ 
        [](auto& f) -> void {
            f.func();
        }(f);
    }
};

struct foo { // definition
    void func() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};


int main() {
    foo f;
    bar b;
    b.memfun(f);
}

重点是明确地写 return 类型 void。 如果我省略这个,那么编译器 g++ 和 clang++ 都会在 f.func(); 处输出错误 "ember access into incomplete type 'foo'"。如果我添加 void return 类型,g++ 似乎懒惰地实例化了 lambda 表达式的主体。但是 clang++ 仍然输出相同的错误。

结果:

哪个编译器有效?

如果 clang++ 有效,是否有任何方法可以惰性地实例化 lambda 表达式的主体,类似于等效的 struct lambda

具有成员函数模板方法的函数对象

我注意到 单独声明和定义方法 并不真正等同于 Lambda 表达式方法 。 lambda表达式的参数是auto&,但是分离声明和定义方式lambda::operation()的参数是foo&.

应该是模板。这是等效的代码:

struct foo; // forward declaration

struct lambda {
    template <typename T>
    void operator()(T& f) {
        f.func();
    }
};

struct bar {
    void memfun(foo& f) {
        lambda()(f);
    }
};

struct foo { // definition
    void func() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

int main() {
    foo f;
    bar b;
    b.memfun(f);
}

运行 演示:https://wandbox.org/permlink/dJ1tqQE8dIMNZqgY

不需要单独声明 lambda::operator()。并在 g++ 和 clang++ 上懒惰地实例化它。如果可能的话,我正在寻找一种使用 lambda 表达式来完成同样事情的方法。

背景(我为什么需要这个?)

我正在使用基于元编程的状态机库 Boost(候选)SML。

https://github.com/boost-experimental/sml/issues/93#issuecomment-283630876

在我看来,您正在寻找的(并且您几乎在最后两种方法中使用的)是一个通用的 lambda。

我是说...

#include <iostream>

// struct foo; // forward declaration (not needed at all)

auto bar = [](auto & f) { f.func(); };

struct foo { // definition
    void func() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

int main() {
    foo f;
    bar(f);
}

诀窍是在 lambda 中接收一个使用 func() 的泛型类型(auto,在你的模板 lambda::operator() 中等同于你最后的方法)。 =27=]

这样,在 bar lambda 定义时,编译器不再需要知道 foo::func() 是如何生成的。

请注意,您的第二种方法也是基于此解决方案,只是过于复杂了。

-- 编辑 --

OP 精确

I cannot replace foo& with auto&. I should add my question background. So I added Background to my question at the last part.

抱歉,我也看了你的背景编辑,我不明白你的确切需求。

无论如何,如果关键是您需要一个接受 foo& 的 lambda,我建议将其写入模板函数中的以下解决方案,但在 foo 定义之后推迟它的生产。

观察最终的 static_assert(),验证 bar 是接受 foo& 的 lambda(更好:验证它可转换为接受 [=17= 的函数指针] 和 return void)

#include <iostream>

// struct foo; // no forward declaration needed

template <typename T>
auto baz ()
 { return [](T & f){ f.func(); }; }

struct foo { // definition
    void func() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

int main() {
    foo f;

    auto bar = baz<foo>();

    bar(f);

    static_assert( std::is_same_v<decltype(+bar), void(*)(foo &)>, "!" );
}

有点离题,但应该让人们知道。由于格式不正确的 NDR,带有模板 "working" 的代码恐怕依赖于未定义的行为。我很脆弱,很容易折断。

[temp.point] (emphasis mine)

1 For a function template specialization, a member function template specialization, or a specialization for a member function or static data member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization and the context from which it is referenced depends on a template parameter, the point of instantiation of the specialization is the point of instantiation of the enclosing specialization. Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization.

8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.

所以首先说明operator()模板有两个实例化点。一个在 bar 之后,另一个在翻译单元的末尾。在第一个实例化点 foo 是不完整的,而在第二个实例化点是完整的。

在这两个实例化点,模板特化具有不同的含义!其一,实例化的特化是病式的,因为它调用了一个不完整类型的成员函数。而在第二个类型完成。正如引用的最后一句话所说,这是格式错误的 NDR。

使其格式正确的唯一方法是稍微移动代码。

struct bar {
    void memfun(foo& f);
};

struct foo { // definition
    void func() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

void bar::memfun(foo& f) {
    [](auto& f) -> void {
        f.func();
    }(f);
}

现在实例化的两个点意义一致,鼻魔的风险就没有了。