Какая разница между типами bar1
и bar2
?
int foo = 10;
auto bar1 = &foo;
auto *bar2 = &foo;
Если оба bar1
и bar2
равны int*
, имеет смысл написать декларатор указателя (*
) в объявлении bar2
?
Какая разница между типами bar1
и bar2
?
int foo = 10;
auto bar1 = &foo;
auto *bar2 = &foo;
Если оба bar1
и bar2
равны int*
, имеет смысл написать декларатор указателя (*
) в объявлении bar2
?
Объявления в точности эквивалентны. auto
работает (почти) так же, как вычитание типа шаблона. Помещение звезды явно делает код более легким для чтения и заставляет программиста понимать, что bar2
является указателем.
Использование auto *
"намерение документов". И auto *p = expr;
может быть выведен правильно, только если expr
возвращает указатель. Пример:
int f();
auto q = f(); // OK
auto *p = f(); // error: unable to deduce 'auto*' from 'f()'
В этом конкретном примере оба 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;
}
Как говорили другие, они будут генерировать один и тот же код. Звездочка представляет собой линейный шум (и затрудняет переход от необработанных указателей к интеллектуальным указателям, если, например, &foo
заменяется на get_foo()
). Если вы хотите быть явным, то, во всяком случае, быть явным; но когда вы используете вывод типа, просто дайте компилятору выполнить свою работу. Отсутствие звездочек не означает, что объект не является указателем.
Существует большая разница, если вы используете квалификаторы 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
Не имеет значения, насколько интерпретируется код С++; вы можете писать все, что захотите. Тем не менее, существует вопрос о стиле и удобочитаемости. Как правило, вы не должны скрывать указатели, ссылки и квалификаторы 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
}