3 回答

TA貢獻1797條經驗 獲得超6個贊
我喜歡這個成語,它有可能變得更干凈,更具表現力。
在標準C ++ 03中,我認為以下方式最容易使用且最通用。(但是沒有太大的改進。大多數情況下可以節省自己重復。)因為模板參數不能成為朋友,所以我們必須使用宏來定義passkey:
// define passkey groups
#define EXPAND(pX) pX
#define PASSKEY_1(pKeyname, pFriend1) \
class EXPAND(pKeyname) \
{ \
private: \
friend EXPAND(pFriend1); \
EXPAND(pKeyname)() {} \
\
EXPAND(pKeyname)(const EXPAND(pKeyname)&); \
EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \
}
#define PASSKEY_2(pKeyname, pFriend1, pFriend2) \
class EXPAND(pKeyname) \
{ \
private: \
friend EXPAND(pFriend1); \
friend EXPAND(pFriend2); \
EXPAND(pKeyname)() {} \
\
EXPAND(pKeyname)(const EXPAND(pKeyname)&); \
EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \
}
// and so on to some N
//////////////////////////////////////////////////////////
// test!
//////////////////////////////////////////////////////////
struct bar;
struct baz;
struct qux;
void quux(int, double);
struct foo
{
PASSKEY_1(restricted1_key, struct bar);
PASSKEY_2(restricted2_key, struct bar, struct baz);
PASSKEY_1(restricted3_key, void quux(int, double));
void restricted1(restricted1_key) {}
void restricted2(restricted2_key) {}
void restricted3(restricted3_key) {}
} f;
struct bar
{
void run(void)
{
// passkey works
f.restricted1(foo::restricted1_key());
f.restricted2(foo::restricted2_key());
}
};
struct baz
{
void run(void)
{
// cannot create passkey
/* f.restricted1(foo::restricted1_key()); */
// passkey works
f.restricted2(foo::restricted2_key());
}
};
struct qux
{
void run(void)
{
// cannot create any required passkeys
/* f.restricted1(foo::restricted1_key()); */
/* f.restricted2(foo::restricted2_key()); */
}
};
void quux(int, double)
{
// passkey words
f.restricted3(foo::restricted3_key());
}
void corge(void)
{
// cannot use quux's passkey
/* f.restricted3(foo::restricted3_key()); */
}
int main(){}
此方法有兩個缺點:1)調用者必須知道它需要創建的特定密鑰。雖然簡單的命名方案(function_key)基本上消除了它,但它仍然可以是一個抽象清潔器(并且更容易)。2)雖然使用宏并不是很困難,但可能會看起來有點難看,需要一組passkey-definitions。但是,在C ++ 03中無法對這些缺點進行改進。
在C ++ 0x中,成語可以達到其最簡單,最具表現力的形式。這是由于可變參數模板和允許模板參數成為朋友。(請注意,2010年之前的MSVC允許模板專家說明符作為擴展;因此可以模擬此解決方案):
// each class has its own unique key only it can create
// (it will try to get friendship by "showing" its passkey)
template <typename T>
class passkey
{
private:
friend T; // C++0x, MSVC allows as extension
passkey() {}
// noncopyable
passkey(const passkey&) = delete;
passkey& operator=(const passkey&) = delete;
};
// functions still require a macro. this
// is because a friend function requires
// the entire declaration, which is not
// just a type, but a name as well. we do
// this by creating a tag and specializing
// the passkey for it, friending the function
#define EXPAND(pX) pX
// we use variadic macro parameters to allow
// functions with commas, it all gets pasted
// back together again when we friend it
#define PASSKEY_FUNCTION(pTag, pFunc, ...) \
struct EXPAND(pTag); \
\
template <> \
class passkey<EXPAND(pTag)> \
{ \
private: \
friend pFunc __VA_ARGS__; \
passkey() {} \
\
passkey(const passkey&) = delete; \
passkey& operator=(const passkey&) = delete; \
}
// meta function determines if a type
// is contained in a parameter pack
template<typename T, typename... List>
struct is_contained : std::false_type {};
template<typename T, typename... List>
struct is_contained<T, T, List...> : std::true_type {};
template<typename T, typename Head, typename... List>
struct is_contained<T, Head, List...> : is_contained<T, List...> {};
// this class can only be created with allowed passkeys
template <typename... Keys>
class allow
{
public:
// check if passkey is allowed
template <typename Key>
allow(const passkey<Key>&)
{
static_assert(is_contained<Key, Keys>::value,
"Passkey is not allowed.");
}
private:
// noncopyable
allow(const allow&) = delete;
allow& operator=(const allow&) = delete;
};
//////////////////////////////////////////////////////////
// test!
//////////////////////////////////////////////////////////
struct bar;
struct baz;
struct qux;
void quux(int, double);
// make a passkey for quux function
PASSKEY_FUNCTION(quux_tag, void quux(int, double));
struct foo
{
void restricted1(allow<bar>) {}
void restricted2(allow<bar, baz>) {}
void restricted3(allow<quux_tag>) {}
} f;
struct bar
{
void run(void)
{
// passkey works
f.restricted1(passkey<bar>());
f.restricted2(passkey<bar>());
}
};
struct baz
{
void run(void)
{
// passkey does not work
/* f.restricted1(passkey<baz>()); */
// passkey works
f.restricted2(passkey<baz>());
}
};
struct qux
{
void run(void)
{
// own passkey does not work,
// cannot create any required passkeys
/* f.restricted1(passkey<qux>()); */
/* f.restricted2(passkey<qux>()); */
/* f.restricted1(passkey<bar>()); */
/* f.restricted2(passkey<baz>()); */
}
};
void quux(int, double)
{
// passkey words
f.restricted3(passkey<quux_tag>());
}
void corge(void)
{
// cannot use quux's passkey
/* f.restricted3(passkey<quux_tag>()); */
}
int main(){}
僅注意樣板代碼,在大多數情況下(所有非功能情況?。┒疾恍枰貏e定義。該代碼通常簡單地為類和函數的任何組合實現慣用語。
調用者不需要嘗試創建或記住特定于該函數的密鑰。相反,每個類現在都有自己唯一的密碼,函數只需在passkey參數的模板參數中選擇允許的密鑰(不需要額外的定義); 這消除了這兩個缺點。調用者只是創建自己的密鑰并使用它調用,而不需要擔心其他任何事情。
- 3 回答
- 0 關注
- 593 瀏覽
添加回答
舉報