2015-05-14 5 views
23

Я читаю this Stack Overflow question, и я добавил конструктор кода из этого вопроса, как follwing,В чем разница между типом и именем в C++?

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. В чем разница между двумя следующими декларациями? Почему я могу получить вывод b.i=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?

+1

Я бы сказал, что нет таких вещей, как частные * типы *. Есть только частные * члены *, и это означает только, что их * имена * недоступны. Сам тип не подлежит контролю доступа. – dyp

+0

dyp is right, имена и типы - это две разные вещи, и они обрабатываются по-разному во время фаз компиляции –

ответ

22

В чем разница между типом и именем

тип не имеет ни один, один или несколько имен. Typedef and alias - это просто средство создания нового имени для типа.

public и private ключевые слова относятся к именам не к базовым типам или элементам.

Чтобы явно объявить объект определенного типа, вам потребуется имя для этого типа. auto не нуждается в этом. Если вы, например, используете неназванный класс as a return type, этот класс не имеет имени, но авто может по-прежнему использоваться на нем.

Тип всегда будет иметь максимум один 'true name'. Даже когда вы используете его с помощью typedef или псевдонима, компилятор использует его под этим именем (или на самом деле является исходной версией этого имени). Таким образом:

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

Печать «класс A». Неименованному объекту нельзя присвоить «истинное имя». Однако при использовании typedef и decltype. Можно создать новое имя. Если это имя используется для создания объектов. typeid().name напечатает новое имя. Если объект не был безымянным, чтобы начать с имени «истинного имени», он будет напечатан.


Сценарии:

  1. Разница заключается в том, что в первом использовании частного объявленное имени. Это незаконно. Это связано с тем, как работают разные способы вывода типа. Как объясняет Скотт Майерс here. Поскольку вызов публичной функции предоставляет этот тип, тип возврата является общедоступным. Однако, Bar сам по себе не является публичным. Это небольшая разница, но в этом причина.

    Это просто решение, которое было сделано в дизайне. Это имеет смысл, иногда вам нужно только, чтобы структура использовалась, когда вы ее возвращаете.

  2. То же самое происходит здесь. Нет никакой разницы, однако Foo::Bar просто недоступен.


Редактировать

Можете ли вы привести пример того, что тип имеет ни названия? это неназванный союз в приведенном выше комментарии?

Как описано here я использую функцию лямбда следующим образом:

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

без использования автоматического или 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".

Примечание:

  • Я воздерживаюсь от использования псевдонима в качестве индикатора для других названий объекта, так как это может привести к путанице с псевдонимом ключевым словом.

  • Большая часть этого я собрал из опыта. Поэтому я, возможно, пропустил несколько бит.

  • Из-за отсутствия лучшего слова я использовал выражение «истинное имя». Если кто-нибудь знает официальное или лучшее слово для этого, я был бы рад услышать это :).

+0

@laurisvr, можете ли вы привести пример, что тип не имеет имен? это неназванный союз в приведенном выше комментарии? И 'int' считается именем типа' int'? – Allanqunzi

+1

@Allanqunzi Я отредактировал мое сообщение, чтобы привести некоторые примеры. И немного разъяснил, как работает именование. – laurisvr

+0

Обратите внимание, что C++ * запрещает * определять новые типы в обратном порядке или типы параметров, как описано в [этом ответе] (http://stackoverflow.com/a/8026593). Однако есть и другие способы создания функции, которая возвращает неназванный тип. (Я добавлю некоторые из них за пару минут.) – dyp

3

Вопрос, который вы связываете, объясняет многое, но в качестве дополнения к тому, что там написано ...

  1. Основное различие заключается в том, что во второй строке auto b=... вы пусть компилятор выводить тип выражения. Вы не можете указать тип, так как имя типа скрыто. Тип, хотя и применим (по крайней мере, от компилятора)

  2. Вы публично публикуете имя типа, чтобы его можно было использовать.

Это очень хороший ответ https://stackoverflow.com/a/13532882/3037915

Чтобы попытаться ответить на этот вопрос в заголовке, вы можете рассмотреть тип, форму и имя типа в качестве имени, используемого для обозначения особая форма. Даже если имя «фигуры» скрыто, форма все еще существует и может использоваться.

6

[Некоторые standardese вперед]

Давайте условимся, что auto вычет работает таким же образом, как шаблон аргумента дедукции:

[dcl.spec.auto]/p7

Если заполнитель является автоматическим спецификатором типа , выведенный тип определяется с использованием th e правила для вывода аргумента шаблона

Шаблоны подпадают под действие two-phase lookup во время компиляции. Контроль доступа применяется к имени поиска в первой фазе

[basic.lookup]/p1

Разрешение перегрузки (13.3) имеет место после имени поиска удалось. Правила доступа (раздел 11) рассматриваются только после успешного поиска имени и разрешения перегрузки функции (если применимо). Только после того, как поиска имен, функция разрешения перегрузки (если это применимо) и проверки доступа преуспел являются атрибуты, введенные декларациями имени в дальнейшем используются при обработке выражения

auto и decltype(auto), как правило, заполнитель для выводимого типа, и правда, [temp.arg]/p3 говорит

имя шаблона-аргумента должен быть доступен в точке, где она используется в качестве шаблона-аргумента

но имена здесь не задействованы, только типы. Контроль доступа применяется к именам, тип может быть сопоставлен с 0, 1 или несколькими именами, и это то, с чем вы имеете дело при использовании auto в приведенном выше коде: он семантически эквивалентен семантике вычета шаблонов, и это по дизайну.

[класс.доступ]/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.


Теперь для ответов:

Case 1 легко согласиться с тем, если вы считаете, что абонент не имеет доступа к имени Foo::Bar.

Case 2 также предоставляет имя вызывающему, поэтому, если вы используете имя typedef'd, ваш код будет с удовольствием скомпилирован.