Подтвердить что ты не робот

Какая разница между типом и именем в С++?

Я читаю этот вопрос о переполнении Stack  , и я добавил конструктор кода из этого вопроса в качестве аргумента,

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. В этом вопросе он заключает, что имя частного доступа недоступно, но тип есть. Итак, какая разница между типом и именем, в общем?

И скажу, что у меня есть два конкретных сценария.

  • Какая разница между следующими двумя объявлениями? Почему я могу получить вывод b.i=5 из auto b = f.Baz();?

    Foo::Bar b = f.Baz();
    auto b = f.Baz();
    
  • Если я добавлю typedef Bar B; в общедоступную часть Foo, в чем разница между следующими?

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

Если существует разница между сценариями 1 и 2?

4b9b3361

Ответ 1

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

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

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

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

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

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

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


Сценарии:

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

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

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


Edit

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

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

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' действительно является именем типа int. Но это никоим образом не единственное имя, int32_t, например, другое имя для одного и того же типа для большинства компиляторов (в других системах int16_t эквивалентно int).

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

Печатает "int".

Примечания:

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

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

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

Ответ 2

[Некоторые стандартные впереди]

Согласитесь, что вывод auto работает так же, как вывод аргумента шаблона:

[dcl.spec.auto]/P7

Если заполнитель является авто type-specifier, выводимый тип определяется с использованием правил вывода аргумента шаблона

Шаблоны подчиняются двухфазному поиску во время компиляции. Контроль доступа применяется для поиска имени на первом этапе.

[basic.lookup]/р1

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

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

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

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

[class.access]/р4

Контроль доступа применяется равномерно ко всем именам, независимо от того, ссылаются ли имена на объявления или выражения. [...] Доступность объекта, упомянутого в 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 sake
};

int main() {
    Foo f;
    std::cout <<"b.i="<< deduce(f.getB())->i <<std::endl; // Prints 'b.i=5'
    return 0;
}

Пример в реальном времени

В коде выше имена не задействованы и, следовательно, управление доступом не применяется. Типы действительно задействованы.

Семантика auto похожа на неявный шаблонный вывод (нормативная формулировка также прямо относится к нему).

У кого-то еще было это сомнение до.


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

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

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

Ответ 3

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

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

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

Это очень приятный ответ fooobar.com/info/50189/...

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