lambda表达的延迟实例化

问题描述 投票:4回答:2

我想在lambda表达式中访问foo::func(),但此时已声明但尚未定义类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);
}

重点是显式地编写返回类型void。如果我忽略了这一点,那么编译器g ++和clang ++都在f.func();输出错误“将ember访问成不完整的类型'foo'”。如果添加void返回类型,则g ++似乎会延迟实例化lambda表达式的主体。但是clang ++仍然输出相同的错误。

结果:

哪个编译器有效?

如果clang ++有效,是否有任何方法可以像等效的struct lambda那样惰性地实例化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

  • [struct with_prop对应于struct foo
  • [struct table对应于struct bar
  • 外部lambda表达式[](with_prop& p) {...对应于void bar::memfun(foo& f)
    • 由于SML重载分辨率,参数foo& f不能为auto& f
  • 内部lambda表达式[](auto& p) -> void { ...对应于[](auto& f) -> void { ...
  • [auto table::operator()() const noexcept不能与声明和定义分开,因为SML在定义operator()()之前使用返回类型。
c++ lambda c++14 lazy-evaluation generic-lambda
2个回答
0
投票

有点切线,但是应该让人们知道。担心模板的代码“工作”依赖于不确定的行为,因为NDR的格式不正确。我很脆弱,很容易折断。

[温度点](强调我的观点)

1对于功能模板专门化,成员函数模板专业化,或成员函数的专业化,或者类模板的静态数据成员,如果专门化为隐式实例化,因为它是从另一个内部引用的模板专门化及其引用的上下文取决于模板参数,专业化是封闭的实例化点专业化。 否则,对于这样的实例化点在名称空间作用域声明之后立即进行专业化处理涉及专业的定义

8功能模板(成员函数)的专门化模板,或类的成员函数或静态数据成员模板在一个模板中可能具有多个实例化点翻译单元,以及实例化点之外如上所述,对于任何具有以下特点的专业化翻译单元内的实例化,翻译结束单元也被视为实例化点。专业化一个类模板在一个模板中最多具有一个实例化点翻译单位。任何模板的专业化可能都有以下几点在多个翻译单元中实例化。 如果有两个不同的点实例化赋予模板专业化不同的含义根据一定义规则,程序格式错误,没有诊断必填

因此,首先它意味着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);
}

现在,实例化的两点在其含义上达成了一致,鼻恶魔的风险也消失了。


0
投票

在我看来,您正在寻找的(以及您在后两种方法中几乎都在使用的)是通用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中接收使用auto的通用类型(lambda::operator(),在您的最后一种方法中,它等同于模板func())。

这样,在bar lambda定义时,编译器不再需要知道如何制作foo::func()

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

-编辑-

OP精确

我无法将foo&替换为auto&。我应该添加我的问题背景。因此,我在最后一部分中将背景添加到我的问题中。

抱歉,还请阅读您的背景编辑,我不明白您的确切需求。

无论如何,如果要点是您需要一个接受foo&的lambda,我提出了以下解决方案,将其写入模板函数中,但将其推迟到foo定义之后才开始生产。

观察最后的static_assert(),以验证bar是接受foo&的lambda(更好的方法:验证可转换为接受foo&并返回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 &)>, "!" );
}
© www.soinside.com 2019 - 2024. All rights reserved.