【问题标题】:What's the difference between type and name in C++?C ++中的类型和名称有什么区别?
【发布时间】:2015-05-14 18:52:22
【问题描述】:

我正在阅读this Stack Overflow question,并在该问题的代码中添加了一个构造函数,如下所示,

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;
}

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

假设我有两个特定的场景。

  1. 以下两个声明有什么区别?为什么我可以从auto b = f.Baz(); 得到输出b.i=5

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

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

如果方案 1 和 2 之间有区别?

【问题讨论】:

  • 我会说不存在私有 types 之类的东西。只有私有的成员,这仅仅意味着他们的名字是不可访问的。类型本身不受访问控制。
  • dyp 是对的,名称和类型是两个不同的东西,在编译阶段它们的处理方式不同

标签: c++ c++11 types


【解决方案1】:

类型和名称有什么区别

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

publicprivate 关键字与名称相关,与基础类型或成员无关。

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

一个类型总是最多有一个'true name'。即使通过 typedef 或别名使用它,编译器也会在这个名称下使用它(或者实际上是这个名称的原始版本)。所以:

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

打印“A 类”。无法为未命名的对象指定“真实名称”。但是,当使用typedefdecltype 时。可以创建一个新名称。如果此名称则用于创建对象。 typeid().name 将打印新分配的名称。如果对象不是以“真实名称”开头的未命名对象,则将打印名称。


场景:

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

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

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


编辑

你能举一个类型没有名字的例子吗?上述评论中的未命名工会就是一个例子吗?

here 所述,我使用如下 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 有点特别,是fundamental type。 'int' 确实是 int 类型的名称。但它绝不是唯一的名称,int32_t,例如,它是大多数编译器上完全相同类型的另一个名称(在其他系统上,int16_t 等同于 int)。

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

打印“int”。

注意事项:

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

  • 我从经验中收集到了大部分内容。所以我可能遗漏了一些内容。

  • 由于找不到更好的词,我使用了“真名”这个表达方式。如果有人知道官方或更好的词,我会很高兴听到它:)。

【讨论】:

  • @laurisvr,你能举一个类型没有名字的例子吗?上述评论中的未命名工会就是一个例子吗? int 是否被认为是 int 类型的名称?
  • @Allanqunzi 我已经编辑了我的帖子以包含一些示例。并进一步阐述了命名的工作原理。
  • 请注意,C++ 禁止在返回或参数类型中定义新类型,如this answer 中所述。然而,还有其他一些方法可以创建返回未命名类型的函数。 (我会在几分钟内添加一些。)
  • @dyp “有些编译器适用于 int 为 16 位的架构。”你说的对。我的愚蠢遗漏。我已经相应地更新了答案。
【解决方案2】:

[前面有一些标准的]

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

[dcl.spec.auto]/p7

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

模板在编译期间受two-phase lookup 的约束。访问控制应用于第一阶段的名称查找

[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;
}

Live example

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

auto的语义类似于隐式模板推演(规范措辞也直接指代)。

Someone else had this doubt before.


现在回答:

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

Case 2 也将名称暴露给调用者,因此如果您使用 typedef 的名称,您的代码将顺利编译。

【讨论】:

    【解决方案3】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2021-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-30
      • 2010-10-02
      • 1970-01-01
      • 1970-01-01
      • 2010-09-07
      相关资源
      最近更新 更多