繁体   English   中英

构造函数参数访问在建对象的成员字段

[英]Constructor parameter access member field of object under construction

我有以下具有成员函数指针的结构。 我的问题是,有没有办法让我传递给它的 lambda 引用它自己的成员变量? IE

struct Foo
{
   int aVar{1};
   int (*funcPtr)(int);
   
   Foo(int (*func)(int)) : funcPtr(func) {};
};

auto bar1 = Foo([](int a) {std::cout << a;}) // ok
auto bar2 = Foo([](int a) {std::cout << a + bar2.aVar;}) // Not ok but is there a way to access the member variable of the object currently being defined?

我可以达到这种效果吗?

我想在这里实现的是根据您传入的 lambda 自动生成对象的过程。例如:上面的bar2是一个可以返回任何内容加上其存储值的对象。 即我希望bar2.funcPtr(5) == 5 + bar2.aVar成为类的不变量。 将来我可能需要另一个可以返回任何减去其存储值的对象,并且我只需要传递相应的 lambda 来执行此操作(如果 lambda 可以访问成员字段),而无需定义新类或方法。

Foo( /* ... */ )

正如在所示代码中使用的,这是一个构造函数调用。 这是在构建一个新对象,就在那里。

在创建新对象并调用其构造函数之前,必须评估构造函数的参数。 这就是 C++ 的工作方式。 没有替代方案或解决方法,最终首先构造对象,然后才评估其构造函数的参数。

出于这个原因,作为参数传递给构造函数的 lambda 在逻辑上是不可能的,“引用它自己的成员变量”。 在这一点上,没有任何东西具有“自己的成员变量”。 对象的构造尚未开始,您不能引用不存在的对象或对象的成员。 在首先评估构造函数的参数之前,无法构造对象。 C++ 不能以这种方式工作。

你需要为你的班级想出一些替代机制。 此时你会发现另一个致命的设计缺陷,它注定了所展示的方法:

int (*funcPtr)(int);

这是一个普通的函数指针。 为了让 lambda 以某种形式或方式引用与其相关的对象,它必须捕获该对象(最有可能是通过引用)。 但是,捕获(按值或引用)的 lambda不能转换为普通函数指针 只有无捕获的 lambda 表达式才能转换为普通指针。

至少,这必须是std::function

现在它是一个std::function ,您可以在 lambda 中通过引用捕获其对象,并将其分配给std::function

但这还不是全部,您还必须解决另一个问题:为了使所有这些工作正常进行,对象不再可能以任何方式移动或复制 这是因为 lambda 捕获了对构造的原始对象的引用,句号。

并且构造对象被复制或移动的事实不会以某种形式或方式修改 lambda,因此它现在神奇地捕获对原始对象的副本或移动实例的引用。

这些都不是不可克服的问题,但它们需要大量的工作来解决,以便得到一个结构良好的 C++ 程序。

lambda 必须有签名int(int)但你的 lambda 有签名void(int)所以这是第一个问题。

另一个是 lambda 必须捕获bar2 你可以使用std::function

#include <iostream>
#include <functional>

struct Foo {
   int aVar{1};

   std::function<int(int)> funcPtr;
   
   Foo(std::function<int(int)> func) : funcPtr(func) {};

   int call(int x) { return funcPtr(x); }
};

int main() {
    Foo bar2([&](int a) { return a + bar2.aVar; });

    std::cout << bar2.call(2);   // prints 3
}

更实用的解决方案是不将 lambda 绑定到最初为其创建的实例,而是让它接受Foo&作为参数(而不是捕获它)。 使用这种方法移动它不会变得如此麻烦。

例子:

struct Foo {
   int aVar;

   int(*funcPtr)(Foo&, int); // takes a Foo& as an argument
   
   Foo(int x, int(*func)(Foo&, int)) : aVar(x), funcPtr(func) {};

   int call(int x) { return funcPtr(*this, x); }
};

int main() {
    Foo bar2(10, [](Foo& f, int a) { return a + f.aVar; });

    std::cout << (bar2.call(5) == bar2.aVar + 5) << '\n';    // true

    Foo bar3(20, [](Foo& f, int a) { return a * f.aVar; });

    std::cout << bar2.call(2) << '\n'; // 10 + 2 = 12
    std::cout << bar3.call(2) << '\n'; // 20 * 2 = 40

    std::swap(bar2, bar3);

    std::cout << bar2.call(2) << '\n'; // swapped, now 40
    std::cout << bar3.call(2) << '\n'; // swapped, now 12
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM