3

我正在尝试声明一个类“Lambdas”,它将向另一个类“Test”提供 lambdas(及其类型信息)。Lambdas 还持有对具体 Test 实例的“this”引用,以便在 lambdas 内访问 Test 公共成员。我这样做是为了定义 lambdas 一次,然后通过 decltype() 在其他任何地方推断类型但我得到错误:成员访问不完整类型:

template <typename T>
struct LambdasInstances {
    T * self;
    explicit LambdasInstances(T * p) : self(p) {} // CAPTURE Test "this"

    auto genLambda1() {
        return [=](int x){
            self->testVar; // ERROR: Member access to incomplete type
        };
    }
};

class Test3 {
public:
    LambdasInstances<Test3> instances;
    int testVar;

    Test3() : instances(this) {}

    decltype(instances.genLambda1()) varLambda = instances.genLambda1();

    void useLambda() { varLambda(123); }
};

但是如果我要在外部定义 genLambda(),那么我会遇到另一个问题 - 错误:在定义之前不能使用推导类型的 genLambda()!:

template <typename T>
struct LambdasInstances {
    T * self;
    explicit LambdasInstances(T * p) : self(p) {}
    auto genLambda1(); // would be defined after Test3 declaration
};


class Test3 {
public:
    int testVar;
    LambdasInstances<Test3> instances;
    Test3() : instances(this) {}
    decltype(instances.genLambda1()) varLambda = instances.genLambda1();
};

// IF WE DEFINE AFTER :: ^ genLambda() with deduced type cannot be used before its defined!
template< typename T>
auto LambdasInstances<T>::genLambda1() {
    return [=](int x){
        self->testVar;
    };
}
4

1 回答 1

1

编译器可能需要定义可用的整个类型才能知道成员的偏移量(例如,在表达式self->testVar中,编译器必须知道 的偏移量testVar),但它可能无法知道特定的偏移量成员,直到它得到整个定义,因为编译器必须知道你的结构/类的对齐方式(我什至猜想在计算成员之间的填充时可能涉及一些不直接的逻辑)在所有成员的知识之后,看到这个,这完全是编译器和平台特定的。

所以回到你的问题。您告诉编译器将Test3with定义genLambda1为成员,这是一个必须知道 member 偏移量的 lambda testVar。看起来很容易,对吧?但是 的偏移量testVar取决于整体的定义Test3(参见上面的段落)——我们在循环中。

你会说:“嘿,愚蠢的编译器,我只给出了一个指向 lambda 的指针,而不是一个你必须知道 `Test3 的整个大小的值的副本,你为什么要阻止我这样做?”。相当合理的问题,因为理论上编译器可以稍后解决偏移,但似乎编译器不够聪明。标准说:

lambda 表达式的复合语句产生函数调用运算符的函数体 (8.4) ...

这基本上说 lambda body 是函数体,但是在函数体中你不能有不完整的类型,对吧?Lambda 对 C++ 来说是相对较新的,并不是所有的极端情况都被详细说明,所以我们希望在将来这个问题会得到解决,当然编译器会更复杂,标准也会更复杂。

对于您的问题,我看到以下解决方案:

template <typename T>
struct LambdasInstances {
  explicit LambdasInstances(T* p) : _lambda([=](int x) { return p->testVar; }) {}

  auto genLambda1() { return _lambda; }

private:
  std::function<void(int)> _lambda;
};

class Test3 {
public:
  int testVar;
  LambdasInstances<Test3> instances;

  Test3() : instances(this) {}

  decltype(instances.genLambda1()) varLambda = instances.genLambda1();
};

int main() {
  Test3 test3;
  Test3* test3_ptr;
  LambdasInstances<Test3> instances(&test3);
  auto lambda = [=](int x) { return test3_ptr->testVar; };
  std::function<void(int)> functor = lambda;
  cerr << sizeof(Test3) << endl;
  cerr << sizeof(LambdasInstances<Test3>) << endl;
  cerr << sizeof(lambda) << endl;
  cerr << sizeof(functor) << endl;
  return 0;
}

不同之处在于它std::function为您提供了一个抽象级别,可以保护类型LambdasInstances::genLambda1不受Test3. 不幸的是,正如您从main输出中看到的那样,该函数比 lambda 占用更多的内存。如果这不能满足您的需求,我建议您修改设计,并且您可能会在 lambda 时代之前的旧技术中找到一些东西。

于 2017-06-04T00:12:55.930 回答