2012-08-13 105 views
19

有這樣一類:爲什麼在lambda中不能使用私有方法?

class A { 
public: 
    bool hasGrandChild() const; 

private: 
    bool hasChild() const; 
    vector<A> children_; 
}; 

爲什麼不可能在方法hasGrandChild()這樣定義的lambda表達式使用的私有方法hasChild()

bool A::hasGrandChild() const { 
    return any_of(children_.begin(), children_.end(), [](A const &a) { 
     return a.hasChild(); 
    }); 
} 

編譯器將發出錯誤的方法hasChild()是範圍內私人。有什麼解決方法嗎?

編輯: 看來,我發佈它的代碼原本的作品。我認爲這是相當的,但does not work on GCC更多這樣的代碼:

#include <vector> 
#include <algorithm> 

class Foo; 

class BaseA { 
protected: 
    bool hasChild() const { return !children_.empty(); } 
    std::vector<Foo> children_; 
}; 

class BaseB { 
protected: 
    bool hasChild() const { return false; } 
}; 

class Foo : public BaseA, public BaseB { 
public: 
    bool hasGrandChild() const { 
    return std::any_of(children_.begin(), children_.end(), [](Foo const &foo) { 
     return foo.BaseA::hasChild(); 
     }); 
    } 
}; 

int main() 
{ 
    Foo foo; 
    foo.hasGrandChild(); 
    return 0; 
} 

似乎有是完全合格的名稱作爲this does not work,但this works一個問題。

+1

閉合類型沒有關係到你的類'A',所以自然就不能訪問'A'的非公共成員。它也不可能,因爲它的名字是不可知的,所以你甚至不能把它變成一個「朋友」。 – 2012-08-13 12:09:23

+2

只是我還是在gcc上做這個工作? http://ideone.com/333qw – pmr 2012-08-13 12:15:30

+0

@pmr:是的,它似乎可以在較老的GCC中工作,但不適用於較新的GCC。 – 2012-08-13 12:28:24

回答

27

這似乎只是在特殊情況下,海灣合作委員會的錯誤,當拉姆達試圖訪問從父類中使用完全合格的名稱保護成員。 This does not work

class Base { 
protected: 
    bool hasChild() const { return !childs_.empty(); } 
    std::vector<Foo> childs_; 
}; 

class Foo : public Base { 
public: 
    bool hasGrandChild() const { 
    return std::any_of(childs_.begin(), childs_.end(), [](Foo const &foo) { 
     return foo.Base::hasChild(); 
    }); 
    } 
}; 

,但this works

class Foo : public Base { 
public: 
    bool hasGrandChild() const { 
    return std::any_of(childs_.begin(), childs_.end(), [](Foo const &foo) { 
     return foo.hasChild(); 
    }); 
    } 
}; 

根據C++ 11,5.1.2/3:

的類型λ-表達的(這也是封閉對象的類型)是一個唯一的,未命名的非結合類類型 - 稱爲 閉包類型 - 其屬性如下所述。此類類型 不是聚合(8.5.1)。 閉包類型在 最小塊作用域,類作用域或包含 相應的lambda表達式的命名空間作用域中聲明。

然後C++ 11,11.7/1:

嵌套類是一個部件,因此具有相同的訪問權限 任何其他成員。

所以提到的函數本地lambda應該具有與該類的任何其他成員相同的訪問權限。因此它應該能夠從父類調用受保護的方法。

+1

爲什麼它*應該*工作的很好的解釋。 (爲什麼大多數其他答案在這裏是錯誤的。)+1 – etherice 2013-08-20 13:06:16

0

這是不可能的,因爲lambda不是類的一部分。這與創建一個不在類的函數相同,並且調用它而不是創建一個lambda。當然,它無法訪問私人會員。

9

標準(C++ 11,§5.1.2/ 3)指出,

的類型λ-表達式(其也是 封閉對象的類型)是一個獨特的,未命名的非工會類型 - 稱爲 閉合類型

因爲它是不是一個A一個friend獨特的類類型,它並沒有獲得A的私有成員。

編譯器在這裏做的是創建一個類類型,它具有適當的成員來存儲任何捕獲的變量,一個合適的operator()等 - 這正是你想要在C++ 03中模擬lambdas時自己寫的。這種類型肯定無法訪問private成員,這可能會更容易看出爲什麼存在限制 以及爲什麼沒有解決方法

更新有關可能的解決方法:

倒不如說「有使用Lambda沒有解決方法」,因爲一般的解決方法是存在的,儘管他們需要你放棄便捷的lambda語法。例如,您可以:

  1. 編寫明確捕捉this它需要任何其他當地人(由下面的Björn博動的評論啓發)沿局部類的類型。
  2. 編寫一個private方法來代替lambda表達式,並將其作爲回調函數傳遞(例如,爲了方便起見使用std::bind)。如果您想捕獲this以外的當地人,則可以在呼叫站點使用更多std::bind來執行此操作。
+2

雖然(http://ideone.com/AvsBE)可以使用本地'struct'。你能解釋一下這個區別嗎? – 2012-08-13 12:13:17

+0

@BjörnPollex:這是一個* local *'struct',這就是爲什麼它可以訪問其包含類型的私有成員(引用該書:*本地類在封閉範圍內,並且具有相同的 可以像封閉函數一樣訪問函數外部的名字*)。你可以說這是一個功能性的解決方法。 – Jon 2012-08-13 12:15:28

+0

@Jon:這並不能解釋爲什麼編譯器不會簡單地將閉包類型作爲它所屬類的一個朋友。如果它可以隱式捕獲「this」,從而有效地扮演成員函數的角色,則它應該能夠訪問其他私有成員。 – 2012-08-13 12:20:12

3

解決方法:

typedef bool (A::*MemFn)(void) const; 

bool A::hasGrandChild() const { 
    MemFn f = &A::hasChild; 
    return any_of(childs_.begin(), childs_.end(), [=](A const &a) { 
      return (a.*f)(); 
    }); 
} 
+1

等一下。爲什麼heck不會只是'any_of(begin,end,std :: mem_fn(&A :: hasChild))'而不是?除了輕微的表現處罰。 – pmr 2012-08-13 13:27:30

+0

@pmr - 這主要是爲了說明如何訪問lambda表達式中的私有成員函數。 – Henrik 2012-08-13 13:30:28

+0

@pmr:性能損失?我不期待一個;對於一個合理的編譯器來說,'f'不會改變是很明顯的。 – MSalters 2012-08-14 07:13:10

3

您可以明確捕獲this並使其成爲可以訪問私有成員的「成員lambda」。

例如,請考慮下面的示例:

#include <iostream> 
class A { 
private: 
    void f() { std::cout << "Private"; } 
public: 
    void g() { 
     [this] { 
      f(); 
      // doesn't need qualification 
     }(); 
    } 
}; 
class B { 
private: 
    void f() { std::cout << "Private"; } 
public: 
    void g() { [] { f(); }(); } // compiler error 
}; 
int main() { 
    A a; 
    a.g(); 
} 
相關問題