Автоматическая декларация С++ 11 с декларатором указателя и без него

Какая разница между типами bar1 и bar2?

int foo = 10;
auto bar1 = &foo;
auto *bar2 = &foo;

Если оба bar1 и bar2 равны int*, имеет смысл написать декларатор указателя (*) в объявлении bar2?

Ответ 1

Объявления в точности эквивалентны. auto работает (почти) так же, как вычитание типа шаблона. Помещение звезды явно делает код более легким для чтения и заставляет программиста понимать, что bar2 является указателем.

Ответ 2

Использование auto * "намерение документов". И auto *p = expr; может быть выведен правильно, только если expr возвращает указатель. Пример:

int f();

auto q = f(); // OK

auto *p = f(); // error: unable to deduce 'auto*' from 'f()'

Ответ 3

В этом конкретном примере оба bar1 и bar2 совпадают. Это вопрос личных предпочтений, хотя я бы сказал, что bar2 легче читать.

Однако это не относится к ссылкам, как показано в этом примере:

#include <iostream>
using namespace std;

int main() {
    int k = 10;
    int& foo = k;
    auto bar = foo; //value of foo is copied and loses reference qualifier!
    bar = 5; //foo / k won't be 5
    cout << "bar : " << bar << " foo : " << foo << " k : " << k << endl;
    auto& ref = foo;
    ref = 5; // foo / k will be 5
    cout << "bar : " << bar << " foo : " << foo << " k : " << k;
    return 0;
}

Ответ 4

Как говорили другие, они будут генерировать один и тот же код. Звездочка представляет собой линейный шум (и затрудняет переход от необработанных указателей к интеллектуальным указателям, если, например, &foo заменяется на get_foo()). Если вы хотите быть явным, то, во всяком случае, быть явным; но когда вы используете вывод типа, просто дайте компилятору выполнить свою работу. Отсутствие звездочек не означает, что объект не является указателем.

Ответ 5

Существует большая разница, если вы используете квалификаторы const:

int i;

// Const pointer to non-const int
const auto ip1 = &i; // int *const
++ip1; // error
*ip1 = 1; // OK

// Non-const pointer to const int
const auto* ip2 = &i; // int const*
++ip2; // OK
*ip2 = 1; // error

Ответ 6

Не имеет значения, насколько интерпретируется код С++; вы можете писать все, что захотите. Тем не менее, существует вопрос о стиле и удобочитаемости. Как правило, вы не должны скрывать указатели, ссылки и квалификаторы CV и, возможно, даже интеллектуальные указатели, в псевдонимах типов, так как это затрудняет понимание читателем того, что происходит. Типичные псевдонимы должны упаковывать семантически релевантный тип содержимого, тогда как классификаторы и модификаторы должны оставаться видимыми. Поэтому предпочитайте следующее:

 using Foo = long_namespace::Foobrigation<other_namespace::Thing>;
 using MyFn = const X * (int, int);

 std::unique_ptr<Foo> MakeThatThing(MyFn & fn, int x)   // or "MyFn * fn"
 { 
     const auto * p = fn(x, -x);
     return p ? p->Create() : nullptr;
 }

И не говори:

using PFoo = std::unique_ptr<Foo>;   // just spell it out
using MyFn = int(&)(int, int);       // unnecessary; & is easy to spell
auto p = fn(x, -x);                  // Don't know that p is a pointer

Обратите внимание, что эталонные квалификаторы (в отличие от указателей) действительно изменяют тип объявляемой переменной, поэтому они не являются необязательными:

X & f();
auto a = f();    // copy!
auto & b = f();  // b is the same as the return value of f()

Наконец, добавление явных указателей указателя const может помочь const-correctness. Рассмотрим следующий пример, в котором контейнер содержит указатели-to-mutable, но нам нужен только доступ к const. Просто auto * выводит указатель на mutable, чего мы можем избежать, явно указав const:

std::vector<X*> v = /* ... */;
for (const auto * p : v)
{
    observe(p->foo());   // no need for a mutable *p
}