繁体   English   中英

C ++中的类型和名称有什么区别?

[英]What's the difference between type and name in C++?

我正在阅读这个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;
}

代码输出bi=5 在那个问题中,它得出结论,私有的名称是不可访问的,但类型是。 那么类型和名称之间的区别通常是什么?

并说我有两个特定的场景。

  1. 以下两个声明之间的区别是什么? 为什么我可以从auto b = f.Baz();获得输出bi=5 auto b = f.Baz();

     Foo::Bar b = f.Baz(); auto b = f.Baz(); 
  2. 如果我添加typedef Bar B; Foo的公共部分,以下是什么区别?

      Foo::Bar b = f.Baz(); Foo::B b = f.Baz(); 

如果方案1和2之间存在差异?

类型和名称之间有什么区别

类型没有,一个或多个名称。 Typedef 和别名只是为类型创建新名称的方法。

publicprivate关键字与不是基础类型或成员的名称相关。

为了显式声明特定类型的对象,您需要该类型的名称。 auto不需要这个。 例如,如果您使用未命名的类作为返回类型 ,则此类没有名称,但仍可以在其上使用auto。

类型最多只能包含一个“ 真实名称 ”。 即使通过typedef或别名使用它,编译器也会以此名称(或实际上是此名称的原始版本)使用它。 所以:

class A {};
typedef A B;
std::cout << typeid(B).name();

打印“A级”。 未命名的对象不能被赋予“真实姓名”。 但是,使用typedefdecltype 可以创建新名称。 如果此名称随后用于创建对象。 typeid().name将打印新分配的名称。 如果对象未命名以“真实姓名”开头,则将打印该名称。


场景:

  1. 不同之处在于,您首先使用的是私有声明的名称。 哪个是非法的。 这与类型推导的不同方式如何工作有关。 正如Scott Meyers 在这里解释的那样。 由于公共函数调用提供此类型,因此返回类型是公共的。 但是, Bar本身并不公开。 这是一个小差异,但这就是原因。

    这只是在设计中做出的决定。 这是有道理的,有时您只需要在返回时使用结构。

  2. 这同样如此。 没有区别,但是Foo::Bar根本无法访问。


编辑

你能给出一个类型没有名字的例子吗? 是上面评论中未命名的联盟的一个例子吗?

如此处所述我使用lambda函数如下:

auto f = [] () -> struct {int x, y ; } { return { 99, 101 } ; } ;

如果不使用auto或decltype,就无法创建变量f 因为它的类型没有名字。 另一个没有名字的类型的例子。

struct foo
{
    struct{
        int x;
        int y;
    } memberVar;
};

允许您执行以下操作:

foo bar;

auto baz = bar.memberVar;

std::cout << baz.x;

当然,这导致一堆初始化的东西,但你明白了:)。 这里的memberVar类型是未命名的。 无法明确定义baz

并且int被认为是int类型的名称?

int有点特殊,是一种基本类型 'int'确实是int类型的名称。 但它绝不是唯一的名称, int32_t ,例如在大多数编译器上是完全相同类型的另一个名称(在其他系统上int16_t相当于int )。

std::cout << typeid(int32_t).name(); 

打印“int”。

笔记:

  • 我已经避免使用别名作为对象其他名称的指示符,因为这可能会导致与alias关键字混淆。

  • 我从经验中收集了大部分内容。 所以我可能错过了一些东西。

  • 由于缺乏更好的词,我使用了“真实姓名”这个词。 如果有人知道官方或更好的话,我会很高兴听到它:)。

[前面的一些标准]

让我们同意auto扣除的工作方式与模板参数推导相同:

[dcl.spec.auto] / P7

如果占位符是自动类型说明符,则使用模板参数推导的规则确定推导的类型

模板在编译期间需要进行两阶段查找 访问控制适用于第一阶段的名称查找

[basic.lookup] / P1

名称查找成功后,将发生重载分辨率(13.3)。 访问规则(第11条)仅在名称查找和功能重载解析(如果适用)成功后才被考虑。 只有在名称查找之后,函数重载解析(如果适用)和访问检查成功才会在表达式处理中进一步使用名称声明引入的属性

autodecltype(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
}

为了说服自己以下内容,请参阅相同的代码,包含模板参数推导(概念上等同于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;
}

实例

在上面的代码中,不涉及名称,因此访问控制不适用。 确实涉及类型。

auto的语义就像隐式模板推导(规范性措辞也直接引用它)。

其他人之前有过这样的疑问


现在回答:

如果您认为调用者无法访问名称Foo::Bar则很容易同意Case 1

Case 2将名称暴露给调用者,因此如果您使用typedef'd名称,您的代码将很乐意编译。

你链接的问题解释了很多,但作为对那里写的内容的补充......

  1. 主要的区别在于,在第二行auto b=...你让编译器推导出表达式的类型。 您无法指定类型,因为隐藏了类型的名称。 虽然类型可用(至少来自编译器)

  2. 您公开显示该类型的名称,以便可以使用它。

这是一个非常好的答案https://stackoverflow.com/a/13532882/3037915

要尝试回答标题中的问题,您可以将类型视为形状,并将类型名称视为用于引用特定形状的名称。 即使隐藏了“形状”的名称,形状仍然存在并且可以使用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM