2012-10-20 190 views
16

從博客文章Access to private members: Safer nastiness通過Johannes Schaub - litb訪問私有成員模板招

template<typename Tag, typename Tag::type M> 
struct Rob { 
    friend typename Tag::type get(Tag) { 
    return M; 
    } 
}; 

// use 
struct A { 
    A(int a):a(a) { } 
private: 
    int a; 
}; 

// tag used to access A::a 
struct A_f { 
    typedef int A::*type; 
    friend type get(A_f); 
}; 

template struct Rob<A_f, &A::a>; 

int main() { 
    A a(42); 
    std::cout << "proof: " << a.*get(A_f()) << std::endl; 
} 

如何get功能,可從a對象,因爲它不是內class A定義打電話?

編輯:

我不明白爲什麼要必須有標籤的參數,而不是a.*get<A_f>() => OK它是由於ADL機制

+0

似乎是一個奇怪的錯誤,當談到論證根據查找(ADL),我現在對我的黑莓,不能用它玩 - 但如果它編譯應該(從我的角度來看)被視爲一個錯誤。 –

+0

有沒有人證實這個可以在gcc以外的編譯器上工作? – hexist

+1

@hexist我剛剛在Clang(3.1)和Intel C++(13.0.0)上對此進行了驗證。 –

回答

6

你是不是叫從aget!實際上得到的回報是指向A中的成員的類指針,其類型爲int A::*,因此您需要使用A的實例來訪問該值。

例如讓我用一下你的代碼:

struct A { 
    A(int a):a(a) { } 
    int b; 
private: 
    int a; 
}; 
void test() { 
    auto p = &A::b; 
    std::cout << a.*p << std::endl; 
} 

難道我叫p從內aa沒有p,這正是您的代碼中發生的情況,get函數返回&A::a並且您使用a來讀取它的值!就這樣,沒有什麼是錯的,我認爲它會在所有編譯器中編譯。

這裏的另一個問題是:爲什麼C++允許使用私有成員A來聲明模板。 C++標準說:

14.7.2p8通常的訪問檢查規則並不適用於用來指定明確的實例名稱。 [注意:特別是,功能聲明符中使用的參數和名稱(包括 參數類型,返回類型和異常規範)的模板 可能是 通常不可訪問的私有類型或對象,而模板可能是成員的 模板或將 通常不會訪問成員函數。]

但是,如果你嘗試實例甚至typedef指定的模板,那麼你得到一個錯誤。 讓我們稍微修改一下你的例子:

struct A { 
private: 
    int a; 
    friend void f(); 
}; 

// Explicit instantiation - OK, no access checks 
template struct Rob<A_f, &A::a>; 

// Try to use the type in some way - get an error. 
struct Rob<A_f, &A::a> r;   // error 
typedef struct Rob<A_f, &A::a> R; // error 
void g(struct Rob<A_f, &A::a>);  // error 

// However, it's Ok inside a friend function. 
void f() { 
    Rob<A_f, &A::a> r;    // OK 
    typedef Rob<A_f, &A::a> R;  // OK 
} 
+3

這就是爲什麼我喜歡C++ –

+0

@stackmonster當然我們有一些共同的東西 – BigBoss

+0

然而,在所有正常使用情況下,您不能接受私人會員的地址,他正在使用模板欺騙來解決這個問題。 – hexist

0

這是合法的,因爲朋友的功能總是在全球範圍,即使你在課堂上實施它們。換句話說,這樣的:

class A 
{ 
    friend void go() {} 
}; 

是這個的快捷方式:

class A 
{ 
    friend void go(); 
}; 

void go() {} 
+2

但是他從A – hexist

+0

以外的朋友那就是他的意思是全球範圍 –

+0

你是對的,這個答案確實回答OP的問題,他爲什麼可以調用'get' ...雖然OP需要問的隱藏問題這就是爲什麼他可以用這個技巧來解析'A'之外的'&A :: a'。 – hexist