如何访问模板参数的成员? "会员访问不完整类型"

时间:2017-06-03 14:57:17

标签: c++ c++11 lambda type-deduction member-access

我试图宣布一个班级" Lambdas"这会将lambda(及其类型信息)提供给另一个班级"测试"。 Lambdas也持有"这个"参考具体的测试实例,用于访问lambda中的Test公共成员。 我这样做是为了定义lambda一次然后通过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;
    };
}

1 个答案:

答案 0 :(得分:1)

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

回到你的问题。您告诉编译器将Test3定义为genLambda1作为成员,这是一个必须知道成员testVar的偏移量的lambda。看起来很简单吧?但是testVar的偏移取决于整个Test3的定义(参见上面的段落) - 这里我们处于循环中。

你会说:&#34;嘿愚蠢的编译器,我只给出一个指向lambda的指针,而不是一个值的副本,你必须知道`Test3的整个大小,为什么你会阻止我这样做?&#34 ;.这是一个合法的问题,因为理论上编译器可以在以后解决偏移,但似乎编译器不够智能。标准说:

  

lambda-expression的复合语句产生函数调用操作符的函数体(8.4)...

那基本上说lambda体是函数体,但在函数体中你不能有不完整的类型吗? Lambdas对于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多。如果这不能满足您的需求,我建议您修改设计,可能会在lambdas时代之前找到旧技术中的一些东西。