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

Связь между auto и decltype

Есть

auto x = initializer;

эквивалентно

decltype(initializer) x = initializer;

или

decltype((initializer)) x = initializer;

или нет?

4b9b3361

Ответ 1

decltype также учитывает, является ли выражение rvalue или lvalue.

Википедия говорит,

Тип, обозначаемый decltype, может отличаться от типа, выведенного автоматически.

#include <vector>
int main()
{
    const std::vector<int> v(1);
    auto a = v[0];        // a has type int
    decltype(v[0]) b = 1; // b has type const int&, the return type of
                        // std::vector<int>::operator[](size_type) const
    auto c = 0;           // c has type int
    auto d = c;           // d has type int
    decltype(c) e;        // e has type int, the type of the entity named by c
    decltype((c)) f = c;  // f has type int&, because (c) is an lvalue
    decltype(0) g;        // g has type int, because 0 is an rvalue
}

Это в значительной степени объясняет важную разницу. Уведомление decltype(c) и decltype((c)) не совпадают!

И когда-то auto и decltype работают совместно, например, в следующем примере (взято из wiki, и немного изменил):

int& foo(int& i);
float foo(float& f);

template <class T>
auto f(T& t) −> decltype(foo(t)) 
{
  return foo(t);
}

Wikipedia далее объясняет семантику decltype следующим образом:

Аналогично оператору sizeof операнд decltype не оценивается. Неофициально тип, возвращаемый decltype (e), выводится следующим образом:

  • Если выражение e относится к переменной в локальной или пространственной области имен, статической членной переменной или функциональному параметру, то результатом является указанная переменная или параметр типа
  • Если e является вызовом функции или перегруженным вызовом оператора, decltype (e) обозначает объявленный тип возврата этой функции
  • В противном случае, если e является lvalue, decltype (e) является T &, где T - тип e; если e - значение r, результат T

Эта семантика была разработана для удовлетворения потребностей разработчиков общих библиотек и в то же время интуитивно понятна для начинающих программистов, поскольку возвращаемый тип decltype всегда соответствует типу объекта или функции, точно указанному в исходном коде. Более формально, правило 1 применяется к разрозненным выражениям id и выражениям доступа к членам класса. Для вызовов функций выводимый тип является возвращаемым типом статически выбранной функции, определяемой правилами разрешения перегрузки. Пример:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1; // type is const int&&
decltype(i) x2; // type is int
decltype(a->x) x3; // type is double
decltype((a->x)) x4; // type is const double&

Причина разницы между двумя последними вызовами типа decltype заключается в том, что выражение в скобках (a- > x) не является идентификатором id или выражением доступа к члену и поэтому не обозначает именованный объект. Поскольку выражение является значением lvalue, его выведенный тип является "ссылкой на тип выражения" или const double &.

Ответ 2

Это не сработает (и уродливо):

decltype([]() { foo(); }) f = []() { foo(); };

тогда

auto f = []() { foo(); };

будет.

Ответ 3

Это зависит. auto и decltype служат для разных целей, поэтому они не сопоставляются друг с другом.

Правила для auto проще всего объяснить, потому что они такие же, как и для вывода параметров шаблона. Я не буду расширять их здесь, но обратите внимание, что auto& и auto&& также являются некоторыми возможными применениями!

decltype однако имеет несколько случаев, некоторые из которых вы проиллюстрировали выше (информация и цитаты, взятые из n3290, 7.1.6.2 Спецификаторы простого типа [dcl.type.simple]), которые я разделяю на две категории:

  • при использовании того, что Стандарт вызывает "unparenthesized id-expression или unparenthesized class member access"
  • остальное!

Неформально, я бы сказал, что decltype может работать с любыми именами (для первого случая) или выражениями. (Формально и в соответствии с грамматикой decltype работает на выражениях, поэтому рассмотрим первый случай как уточнение, а второй случай - как catch-all.)

При использовании имени с decltype вы получаете объявленный тип этого объекта. Так, например, decltype(an_object.a_member) - это тип члена, который появляется в определении класса. С другой стороны, если мы используем decltype( (an_object.a_member) ), мы попадаем во весь случай, и мы проверяем тип выражения, как оно появляется в коде.

Соответственно, как охватить все случаи ваших вопросов:

int initializer;
auto x = initializer; // type int
// equivalent since initializer was declared as int
decltype(initializer) y = initializer;


enum E { initializer };
auto x = initializer; // type E
// equivalent because the expression is a prvalue of type E
decltype( (initializer) ) y = initializer;


struct {
    int const& ializer;
} init { 0 };
auto x = init.ializer; // type int
// not equivalent because declared type is int const&
// decltype(init.ializer) y = init.ializer;
// not equivalent because the expression is an lvalue of type int const&
// decltype( (init.ializer) ) y = init.ializer;

Ответ 4

auto

auto прост: он будет давать тот же тип, что и вычет параметра шаблона по значению. auto работает равномерно на выражениях.

template <class T>
void deduce(T x);

int &refint();
std::string str();
std::string const conststr();

auto i1 = 1; // deduce(1) gives T=int so int i1
auto i2 = i1; // deduce(i1) gives T=int so int i2
auto i3 = refint(); // deduce(refint()) gives T=int so int i3
const auto ci1 = i1; // deduce(i1) gives T=int so const int ci1
auto i4 = ci1; // deduce(ci1) gives T=int so int i4

auto s1 = std::string(); // std::string s1
auto s2 = str(); // std::string s2
auto s3 = conststr(); // std::string s3

В выражениях С++ не может быть ссылочный тип (refint() имеет тип int not int&).

Обратите внимание, что lvalyeness выражения не является проблемой для выражения справа (справа от знака равенства или чего-то, что копируется вообще). Rvalue 1 обрабатывается как lvalues ​​i1 и refint().

Для параметров значений (то есть не ссылочных параметров) применяется не только преобразование lvalue в rvalue, но и преобразование массива в указатель. const игнорируется.

decltype

decltype - очень полезная функция с ужасным интерфейсом:

decltype действует по-разному на некоторые выражения, определенные в терминах поиска имени и других выражений! Это тип функций, которые заставляют людей ненавидеть С++.

decltype того, что называется

decltype(entity) выполнит поиск по имени и даст объявленный тип объекта. (entity может быть неквалифицированным или квалифицированным идентификатором или доступом к члену, например expr.identifier.)

decltype(f(args)) будет выполнять поиск имен и разрешение перегрузки и выдавать объявленный тип возвращаемого значения функции, а не тип выражения:

extern decltype(refint()) ri1; // int &ri1

Итак, теперь я могу проверить свое понимание языка с помощью decltype:

template <class T, class U>
struct sametype {};

template <class T>
struct sametype<T,T> {typedef int same;};

sametype<T,U>::same существует, если if T и U являются точно такими же.

sametype<decltype (i1), int>::same check_i1;
sametype<decltype (i2), int>::same check_i2;
sametype<decltype (i3), int>::same check_i3;
sametype<decltype (i4), int>::same check_i4;

sametype<decltype (ci1), const int>::same check_ci1;
sametype<decltype (ir1), int&>::same check_ir1;

sametype<decltype (s1), std::string>::same check_s1;
sametype<decltype (s2), std::string>::same check_s2;
sametype<decltype (s3), std::string>::same check_s3;

компилируется отлично, поэтому я не ошибся!

decltype других выражений

В противном случае, если expr не определяется в терминах поиска по имени (не один из приведенных выше случаев), например выражение (expr), decltype реализует определенную функцию (но разработчики С++ не потратили другого ключевые слова на нем.)

decltype(expr) даст тип выражения, украшенного его lxrvalueness (lvalue/xvalue/prvalue-ness):

  • prvalue (чистое rvalue) типа T дает T
  • xvalue типа T дает T&&
  • lvalue типа T дает T&

Это ответное правило вызова функции: if f - это функция с возвращаемым типом

  • T&, выражение f() является lvalue
  • T&&, выражение f() представляет собой значение xvalue
  • голый тип (тип чистого объекта, не ссылка) T, выражение f() является prvalue

а также для бросков: для голого типа (чистый тип объекта, без ссылки) T

  • (T&)expr - это lvalue
  • (T&&)expr - это значение xvalue
  • (T)expr - это prvalue

Ссылка - это кодирование lxrvalueness на типы. decltype выполняет ли это кодирование, чтобы сохранить и передать значение вещей.

Это полезно, когда вы хотите использовать псевдоним выражения: lxrvalueness выражения expr, который является вызовом функции (либо нормальный f(args), либо синтаксис оператора, такой как a @ b), совпадает с lxrvalueness alias() объявлен как decltype(expr) alias();

Это может использоваться для чистой пересылки в общем коде:

// decorated type of an expression
#define EXPR_DEC_TYPE(expr) decltype((expr))

int i;
int &ri = i;
int fi();
int &fri();

EXPR_DEC_TYPE(i) alias_i = i; // int &
EXPR_DEC_TYPE(ri) alias_ri = ri; // int &

EXPR_DEC_TYPE(fi()) alias_fi(); // int alias_fi()
EXPR_DEC_TYPE(fri()) alias_fri(); // int &alias_fri()

Обратите внимание, что EXPR_DEC_TYPE(foo()) составляет declexpr(foo()) по дизайну (в большинстве случаев), но вычисления различны:

  • declexpr(foo(args)) выполняет поиск имени foo, перегружает разрешение, находит объявление, возвращает точный тип возврата объявлено, конец истории

  • EXPR_DEC_TYPE(foo(args)) находит тип объявления, тогда вычисляет

    • тип T выражения, которое является обратным типом голым (без ссылка)

    • lxrvalueness LXR выражения в соответствии с референциальностью объявленного типа возврата: lvalue для ссылки, xvalue r-reference...

    затем он украшает тип T LXR, чтобы получить тип decT:

    • decT T, если LXR = prvalue
    • decT T&&, если LXR = xvalue
    • decT T&, если LXR = lvalue

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

Ответ 5

  • Если initializer - массив, то decltype(x) или decltype((x)) не работайте просто на нем. Однако auto будет выведено указатель.
  • Если initializer - это функция, то применение decltype(fp) будет вывести на тип функции, однако, auto выведет его возвращаемый тип.

Таким образом, вообще говоря, auto нельзя рассматривать как замену любой версии decltype(), которую вы задали.