简体   繁体   中英

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

I am reading this Stack Overflow question , and I added a constructor to the code from that question as the follwing,

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

The code outputs bi=5 . In that question it concludes that the name of the private is not accessible but the type is. So what's the difference between type and name, generally?

And say I have two specific scenarios.

  1. What's the difference between the following two declarations? Why can I get the output bi=5 from auto b = f.Baz(); ?

     Foo::Bar b = f.Baz(); auto b = f.Baz(); 
  2. If I add typedef Bar B; in the public part of Foo , what is the difference between the following?

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

If there a difference between scenario 1 and 2?

What is the difference between type and name

A type has none, one or several names. Typedef and alias are simply means of creating a new name for a type.

public and private keywords relate to names not to the underlying types or members.

In order to explicitly declare an object of a particular type you need a name for that type. auto doesn't need this. If you for example use an unnamed class as a return type , this class has no name but auto can still be used on it.

An type will always have at most one ' true name '. Even when using it through a typedef or alias, the compiler uses it under this name (or actually the raw version of this name). So:

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

Prints "class A". An unnamed object cannot be given a 'true name'. However, when using typedef and decltype . A new name can be created. If this name is then used to create objects. typeid().name will print the newly assigned name. If the object wasn't unnamed to start with the 'true name' name will be printed.


The scenarios:

  1. The difference is that in the first you are using a privately declared name. Which is illegal. This has to do with how the different ways of type deduction work. As Scott Meyers explains here . Since a public function call provides this type, the return type is public. However, Bar on itself is not public. It's a small difference, but that is the reason.

    This simply is a decision that has been made in the design. It makes sense, sometimes you only want a struct to be used when you return it.

  2. The same goes here. There is no difference, however Foo::Bar simply is inaccessible.


Edit

Can you give an example that type has none names? is the unnamed union in the above comment a example of this?

As described here I use a lambda function as follows:

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

Without using auto or decltype there would be no way of creating variable f . Since it's type has no name. Another example of a type without a name.

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

Would allow you to do the following:

foo bar;

auto baz = bar.memberVar;

std::cout << baz.x;

Which results in a bunch of initialized stuff of course, but you get the idea:). The type of memberVar here is unnamed. Making it impossible to define baz explicitly.

And is int considered to be a name for type of int?

int is a bit special, being a fundamental type . 'int' indeed is the name of the type int. But it's by no means the only name, int32_t , for example is another name for the exact same type on most compilers(on other systems int16_t is equivalent to int ).

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

Prints "int".

Notes:

  • I've refrained from using alias as an indicator for other names of an object, since this might cause confusion with the alias keyword.

  • Most of this I have gathered from experience. So I might have missed some bits.

  • By lack for a better word i have used the expression 'true name'. If anybody knows the official or a better word for this i'd be happy to hear it:).

[Some standardese ahead]

Let's agree that auto deduction works in the same way as template argument deduction:

[dcl.spec.auto]/p7

If the placeholder is the auto type-specifier, the deduced type is determined using the rules for template argument deduction

Templates are subject to the two-phase lookup during compilation. Access control is applied to name lookup in the first phase

[basic.lookup]/p1

Overload resolution (13.3) takes place after name lookup has succeeded. The access rules (Clause 11) are considered only once name lookup and function overload resolution (if applicable) have succeeded. Only after name lookup, function overload resolution (if applicable) and access checking have succeeded are the attributes introduced by the name's declaration used further in expression processing

auto and decltype(auto) are usually a placeholder for a deduced type, and it is true that [temp.arg]/p3 says

The name of a template-argument shall be accessible at the point where it is used as a template-argument

but names aren't involved here , only the types. Access control applies to names, a type can be mapped to 0, 1 or multiple names and that's what you're dealing with when using auto in the code above: it is semantically equivalent to the semantics of template deduction and this is by design.

[class.access]/p4

Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions. [...] The accessibility of the entity referred to by the typedef is not considered. For example

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
}

To convince yourself of the following take a look at the same code with template argument deduction involved (conceptually equivalent to the auto version)

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

In the code above names aren't involved and thus access control doesn't apply. Types are indeed involved.

The semantics of auto is like implicit template deduction (the normative wording also directly refers to it).

Someone else had this doubt before .


Now for the answers:

Case 1 is easy to agree with if you consider that the caller has no access to the name Foo::Bar .

Case 2 exposes the name to the caller as well, so if you use the typedef'd name your code will happily compile.

The question you link explains a lot but as a supplement to what is being written there...

  1. The main difference is that in the second line auto b=... you let the compiler deduce the type of the expression. You can't specify the type, as the name of the type is hidden. The type though is usable (at least from the compiler)

  2. You expose the name of the type publicly so it can be used.

This is a very nice answer https://stackoverflow.com/a/13532882/3037915

To try to answer the question in the header, you may consider type as a shape and the name of the type as the name you use to refer to a particular shape. Even if the name of the "shape" is hidden, the shape still exists and can be used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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