我刚刚阅读了Raymond Chen的优秀文章《新旧事物》中的以下文章:https://devblogs.microsoft.com/oldnewthing/20210719-00/?p=105454
我对此有一个问题,最好在下面的代码片段中进行描述。为什么允许初始化"x3"?我看不出下面"x2"one_answers"x3"的初始化之间有任何语义差异。
#include <memory>
class X
{
struct PrivateTag {}; // default constructor is *NOT* explicit
public:
X(PrivateTag) {}
static auto Create() -> std::unique_ptr<X>
{
return std::make_unique<X>(PrivateTag{});
}
};
int main()
{
auto x1 = X::Create(); // ok: proper way to create this
//auto x2 = X(X::PrivateTag{}); // error: cannot access private struct
auto x3 = X({}); // ok: why ?!
}
辅助功能仅限制哪些名称可以显式写出。它决不会阻止使用某个类型。
例如,在返回私有类型的函数上使用decltype
,然后给它一个新名称并通过该名称完全访问它,或者使用模板参数推导给不可访问的类型新的别名,通过这些别名可以访问它们。
你能做的最好的事情就是通过使用链接博客文章中讨论的方法,让用户很难意外地滥用构造函数,但如果用户足够坚定,他们几乎总是可以获得PrivateTag
的完全可访问的别名,并无论如何使用构造函数。
auto x3 = X({});
并没有拼出成员类名,所以它的可访问性并不重要。
auto x2 = X(X::PrivateTag{});
确实拼写了成员类的名称,因此需要考虑可访问性,这使得它的格式不正确,因为PrivateTag
是私有的,在命名它的上下文中是不可访问的。
这里有一个绕过私人标签技巧的例子,无论是否进行了博客文章中建议的调整:
struct any_convert {
template<typename T>
operator T() const {
// Here `T` becomes an alias for `PrivateTag`
// but is completely accessible.
return T{};
}
};
//...
auto x4 = X(any_convert{});