互换的青春
我喜欢这个成语,它有可能变得更干净,更具表现力。在标准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 packtemplate<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 passkeystemplate <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 functionPASSKEY_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(){}仅注意样板代码,在大多数情况下(所有非功能情况!)都不需要特别定义。该代码通常简单地为类和函数的任何组合实现惯用语。调用者不需要尝试创建或记住特定于该函数的密钥。相反,每个类现在都有自己唯一的密码,函数只需在passkey参数的模板参数中选择允许的密钥(不需要额外的定义); 这消除了这两个缺点。调用者只是创建自己的密钥并使用它调用,而不需要担心其他任何事情。