2017-06-03 129 views
3

我試圖聲明一個類「Lambdas」,它將lambdas(及其類型信息)提供給另一個類「Test」。 Lambdas還在「lambda」中持有「this」對具體Test實例的引用,以便訪問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; 
    }; 
} 
+0

你真的需要'實例'成員嗎?鑑於你也有返回的lambda作爲成員。 –

+0

@DanielJour'instances'成員在這裏將Test3的'this'封裝在'LambdasInstances'裏面。我不知道我怎麼能把這個'注入'我的lambda。根據'varLambda'是成員,因爲我想稍後使用'Test3'作爲狀態機,所以如果這有助於(甚至是有效的),我需要一個類可見的名稱來引用'currentState = varLambda;' – barney

+0

Idk,但顯然這是有效的:http://coliru.stacked-crooked。com/a/b63cbf45576c4bf4 –

回答

1

編譯器可能需要提供全類型的定義可以知道的偏移成員(例如在表達式self->testVar中,編譯器必須知道它的偏移量爲testVar),但它可能無法知道特定成員的偏移量,直到它將得到整個定義爲止,因爲編譯器必須知道你的結構/類的對齊方式(我甚至會猜測有些不直接向前在計算成員之間的填充時可能涉及邏輯),這是在所有成員知道之後的,參見this,這完全是編譯器和平臺特定的。

所以回到你的問題。您告訴編譯器將Test3genLambda1一起定義爲成員,即必須知道成員testVar的偏移量的lambda。看起來很簡單,對吧?但是testVar的偏移量取決於整個Test3的定義(參見上面的段落) - 這裏我們處於循環中。你會說:「嘿,愚蠢的編譯器,我只給一個指向lambda的指針,而不是一個值的副本,你必須知道`Test3的整個大小,爲什麼你會阻止我這麼做? 。相當合理的問題,因爲理論上編譯器可以稍後解決偏移量,但似乎編譯器不夠智能。標準說:

The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator ...

基本上說,lambda身體是函數體,但在函數體中,你不能有不完整的類型吧? 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爲您提供了抽象,從保護的Test3定義類型LambdasInstances::genLambda1的水平。不幸的是,從main輸出中可以看到,函數佔用的內存比lambda更多。如果這不能滿足你的需求,我建議修改設計,也許你會在lambdas時代之前的老技術中找到一些東西。

+0

感謝您的評論。如果我使用'std :: function',我需要指定兩次類型信息:在'std :: function'的模板參數''中,同時定義lambda本身。所以我只是想建立一個方案,而我可以自由添加提供任何我想要的lambda的功能。你可以爭辯說''decltype'也是醜陋的,我同意。我希望我可以在類成員中以某種方式使用'auto' ...此外,std :: function還需要虛函數調用和堆分配。我可以直接在'Test'類中直接使用它,並通過類成員初始化語法從'Lambdas'分配 – barney