我正在阅读这个Stack Overflow问题,我在该问题的代码中添加了一个构造函数,如下所示,
class Foo {
struct Bar {
int i;
Bar(int a = 5) :i(a) {};
};
public:
Bar Baz() { return Bar(); }
};
int main() {
Foo f;
// Foo::Bar b = f.Baz(); // error
auto b = f.Baz(); // ok
std::cout <<"b.i="<< b.i<<endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
代码输出b.i=5.在那个问题中,它得出结论,私有的名称是不可访问的,但类型是.那么一般来说,类型和名称之间的区别是什么?
并说我有两个特定的场景.
以下两个声明之间的区别是什么?为什么我能得到的输出b.i=5从auto b = f.Baz();?
Foo::Bar b = f.Baz();
auto b = f.Baz();
Run Code Online (Sandbox Code Playgroud)如果我typedef Bar B;在公共部分添加,Foo以下有什么区别?
Foo::Bar b = f.Baz();
Foo::B b = f.Baz();
Run Code Online (Sandbox Code Playgroud)如果方案1和2之间存在差异?
lau*_*svr 21
类型和名称之间有什么区别
类型没有,一个或多个名称.Typedef 和别名只是为类型创建新名称的方法.
public和private关键字与不是基础类型或成员的名称有关.
为了显式声明特定类型的对象,您需要该类型的名称.auto不需要这个.例如,如果您使用未命名的类作为返回类型,则此类没有名称,但仍可以在其上使用auto.
类型最多只能有一个' 真实名称 '.即使通过typedef或别名使用它,编译器也会以此名称(或实际上是此名称的原始版本)使用它.所以:
class A {};
typedef A B;
std::cout << typeid(B).name();
Run Code Online (Sandbox Code Playgroud)
打印"A级".未命名的对象不能被赋予"真实姓名".但是,使用typedef和时decltype.可以创建新名称.如果此名称随后用于创建对象.typeid().name将打印新分配的名称.如果对象未命名以"真实姓名"开头,则将打印该名称.
场景:
不同之处在于,您首先使用的是私有声明的名称.哪个是非法的.这与类型推导的不同方式如何工作有关.正如Scott Meyers 在这里解释的那样.由于公共函数调用提供此类型,因此返回类型是公共的.但是,Bar它本身并不公开.这是一个小差异,但这就是原因.
这只是在设计中做出的决定.这是有道理的,有时您只需要在返回时使用结构.
这同样如此.没有区别,但是Foo::Bar根本无法访问.
编辑
你能给出一个类型没有名字的例子吗?是上面评论中未命名的联盟的一个例子吗?
如此处所述,我使用lambda函数如下:
auto f = [] () -> struct {int x, y ; } { return { 99, 101 } ; } ;
Run Code Online (Sandbox Code Playgroud)
不使用auto或decltype就没有办法创建变量f.因为它的类型没有名字.另一个没有名字的类型的例子.
struct foo
{
struct{
int x;
int y;
} memberVar;
};
Run Code Online (Sandbox Code Playgroud)
允许您执行以下操作:
foo bar;
auto baz = bar.memberVar;
std::cout << baz.x;
Run Code Online (Sandbox Code Playgroud)
当然,这导致一堆初始化的东西,但你明白了:).memberVar这里的类型是未命名的.无法baz明确定义.
并且int被认为是int类型的名称?
int有点特别,是一种基本类型.'int'确实是int类型的名称.但它绝不是唯一的名称,int32_t例如,在大多数编译器上int16_t 是完全相同类型的另一个名称(在其他系统上相当于int).
std::cout << typeid(int32_t).name();
Run Code Online (Sandbox Code Playgroud)
打印"int".
笔记:
我已经避免使用别名作为对象其他名称的指示符,因为这可能会导致与alias关键字混淆.
我从经验中收集了大部分内容.所以我可能错过了一些东西.
由于缺乏更好的词,我使用了"真实姓名"这个词.如果有人知道官方或更好的话,我会很高兴听到它:).
[前面的一些标准]
让我们同意auto推论的工作方式与模板参数推导相同:
[dcl.spec.auto]/P7
如果占位符是自动类型说明符,则使用模板参数推导的规则确定推导的类型
模板在编译期间需要进行两阶段查找.访问控制适用于第一阶段的名称查找
[basic.lookup]/P1
名称查找成功后,将发生重载分辨率(13.3).访问规则(第11条)仅在名称查找和功能重载解析(如果适用)成功后才被考虑.只有在名称查找之后,函数重载解析(如果适用)和访问检查成功才会在表达式处理中进一步使用名称声明引入的属性
auto和decltype(auto)通常用于推导类型的占位符,这是事实,[temp.arg]/P3说
模板参数的名称可以在用作模板参数的位置访问
但这里不涉及名称,只涉及类型.访问控制适用于名称,类型可以映射到0,1或多个名称,这是您在auto上面的代码中使用时所处理的内容:它在语义上等同于模板推导的语义,这是设计的.
[class.access]/P4
访问控制统一应用于所有名称,无论名称是从声明还是表达式引用.[...]不考虑typedef引用的实体的可访问性.例如
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
}
Run Code Online (Sandbox Code Playgroud)
为了说服自己以下内容,请查看相同的代码,包含模板参数推导(概念上等同于auto版本)
template<class T>
T deduce(T t) {
return t;
}
class Foo {
struct Bar{
int i;
Bar(int a = 5):i(a){};
};
public:
Bar *getB() { return new Bar(5); } // Leaks, doesn't matter for clarity's sake
};
int main() {
Foo f;
std::cout <<"b.i="<< deduce(f.getB())->i <<std::endl; // Prints 'b.i=5'
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,不涉及名称,因此访问控制不适用.确实涉及类型.
语义auto就像隐式模板推导(规范性措辞也直接引用它).
现在回答:
Case 1如果您认为调用者无法访问该名称,则很容易同意Foo::Bar.
Case 2 将名称公开给调用者,因此如果使用typedef'd名称,您的代码将很乐意编译.
| 归档时间: |
|
| 查看次数: |
1106 次 |
| 最近记录: |