我喜欢这个成语,它有潜力变得更加简洁和表达力强。
在标准C++03中,我认为以下方法是最容易使用和最通用的(虽然提升不太大,但可以避免重复)。因为
模板参数不能成为友元,我们必须使用宏来定义传递密钥:
#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)&); \
}
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)
{
f.restricted1(foo::restricted1_key());
f.restricted2(foo::restricted2_key());
}
};
struct baz
{
void run(void)
{
f.restricted2(foo::restricted2_key());
}
};
struct qux
{
void run(void)
{
}
};
void quux(int, double)
{
f.restricted3(foo::restricted3_key());
}
void corge(void)
{
}
int main(){}
该方法有两个缺点:1)调用者必须知道它需要创建的特定密码。虽然一个简单的命名方案(
function_key
)基本上可以消除它,但它仍然可以更加抽象和简单。2)虽然使用宏不是很困难,但可能被看作是有点丑陋的,需要一块密码定义。然而,在C++03中无法改进这些缺点。
在C++0x中,这种习惯用法可以达到最简单和最具表现力的形式。这是由于变长模板和允许模板参数成为友元。(请注意,MSVC pre-2010允许模板友元说明符作为扩展;因此可以模拟这个解决方案):
template <typename T>
class passkey
{
private:
friend T;
passkey() {}
passkey(const passkey&) = delete;
passkey& operator=(const passkey&) = delete;
};
#define EXPAND(pX) pX
#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; \
}
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...> {};
template <typename... Keys>
class allow
{
public:
template <typename Key>
allow(const passkey<Key>&)
{
static_assert(is_contained<Key, Keys>::value,
"Passkey is not allowed.");
}
private:
allow(const allow&) = delete;
allow& operator=(const allow&) = delete;
};
struct bar;
struct baz;
struct qux;
void quux(int, double);
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)
{
f.restricted1(passkey<bar>());
f.restricted2(passkey<bar>());
}
};
struct baz
{
void run(void)
{
f.restricted2(passkey<baz>());
}
};
struct qux
{
void run(void)
{
}
};
void quux(int, double)
{
f.restricted3(passkey<quux_tag>());
}
void corge(void)
{
}
int main(){}
请注意,只有样板代码,在大多数情况下(所有非函数的情况下!),不需要特别定义任何内容。该代码通用且简单地实现了任何类和函数组合的惯用语。
调用者不需要尝试创建或记住特定于函数的通行证。相反,每个类现在都有自己独特的通行证,函数仅选择它将允许的通行证的模板参数(无需额外定义);这消除了两个缺点。调用者只需创建自己的通行证并使用它进行调用,不需要担心其他任何事情。
get_function_pointer()
方法。 - Georg Fritzsche