为什么我可以在私有类型上使用 auto?

下面的代码编译并运行(vc2012 & gcc4.7.2)让我有些惊讶

class Foo {
struct Bar { int i; };
public:
Bar Baz() { return Bar(); }
};


int main() {
Foo f;
// Foo::Bar b = f.Baz();  // error
auto b = f.Baz();         // ok
std::cout << b.i;
}

这段代码编译得很好是正确的吗?为什么是对的?为什么我可以在私有类型上使用 auto,而不能使用它的名称(正如预期的那样) ?

5505 次浏览

在大多数情况下,auto的规则与模板类型推导的规则相同。发布的示例与可以将私有类型的对象传递给模板函数的原因相同:

template <typename T>
void fun(T t) {}


int main() {
Foo f;
fun(f.Baz());         // ok
}

您会问,为什么我们可以将私有类型的对象传递给模板函数?因为只有类型的名称是不可访问的。类型本身仍然是可用的,这就是为什么您可以将其返回到客户机代码。

访问控制应用于 名字:

class A {
class B { };
public:
typedef B BB;
};


void f() {
A::BB x; // OK, typedef name A::BB is public
A::B y; // access error, A::B is private
}

为了补充其他(好的)答案,这里有一个来自 C + + 98的例子,说明这个问题实际上与 auto没有任何关系

class Foo {
struct Bar { int i; };
public:
Bar Baz() { return Bar(); }
void Qaz(Bar) {}
};


int main() {
Foo f;
f.Qaz(f.Baz()); // Ok
// Foo::Bar x = f.Baz();
// f.Qaz(x);
// Error: error: ‘struct Foo::Bar’ is private
}

使用私有类型并不是被禁止的,它只是命名了类型。例如,在所有版本的 C + + 中,创建这种类型的未命名临时文件是可以的。

这个问题已经被费尔南德斯和马蒂尼奥都很好地回答了。

我不能错过用哈利波特来回答问题的机会:

class Wizard
{
private:
class LordVoldemort
{
void avada_kedavra()
{
// scary stuff
}
};
public:
using HeWhoMustNotBeNamed = LordVoldemort;


friend class Harry;
};


class Harry : Wizard
{
public:
Wizard::LordVoldemort;
};


int main()
{
Wizard::HeWhoMustNotBeNamed tom; // OK
// Wizard::LordVoldemort not_allowed; // Not OK
Harry::LordVoldemort im_not_scared; // OK
return 0;
}

Https://ideone.com/i5q7gw

感谢昆汀提醒了我哈里的漏洞。

对于其他需要解决方案的人(例如,声明一个接受私有类型的函数) ,我是这样做的:

void Func(decltype(Foo().Baz()) param) {...}