Ключевые слова auto и decltype – сходства, отличия и нюансы использования. Часть 2. decltype вывод типа простого выражения.



Проблема, затронутая в конце предыдущего раздела до С++11 решалась некоторыми компиляторами с помощью неофициального оператора typeof.

template<typename T, typename S>
void foo(T lhs, S rhs)
{
   typedef typeof(lhs * rhs) product_type;
}

Оператор typeof легализовался как decltype. А вот другая ситуация, когда этот оператор может быть необходим — указание возвращаемого типа в несколько измененном предыдущем примере (не компилируется):

template<typename T, typename S>
decltype(lhs * rhs) multiply(T lhs, S rhs)
{
   return lhs * rhs;
}

Скомпилировать это не удастся, поскольку lhs и rhs использованы внутри decltype до их объявления как параметры функции. Чтобы решить это затруднение, С++11 вводит новый синтаксис вывода типа (trailing return type):


template<typename T, typename S>
auto multiply(T lhs, S rhs) -> decltype(lhs * rhs)
{
    return lhs * rhs;
}

Этот вариант успешно компилируется. Следует заметить, что auto, используемый тут — это не тот auto, о котором речь шла ранее. Использование auto в этом контексте продолжает традицию использования одних и тех же лексических токенов в разных целях. Это несколько напрягает.

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

Посмотрим почему это не так.

Путь, которым decltype выводит тип выражения отличается от пути auto и основывается на рассмотрении двух ситуаций.

1 ситуация.
Выражение, тип которого определяется, является простой переменной или параметром функции (x), или доступом к члену класса (p->m_x). В этом ситуации decltype оправдывает своё имя: он определяет тип выражения как декларированный тип, то есть тип, который мы найдем в исходном коде в точке декларации. Если отвлечься от всех усложнений как typedef, несколько уровней косвенности и инстанцирование шаблонов, речь идет о ситуации, когда тип выражения лексически присутствует в исходном коде.

Правило:
Если expr является простой, непараметризированной переменной, параметром функции или доступом к члену класса, то decltype(expr) есть тип этой переменной, параметра функции или члена класса объявленного в исходном коде.

Приведем несколько примеров — для сравнения тут же приведем примеры с auto. (поведение decltype показано синим, auto – зеленым цветом).

struct S
{
    S(){m_x = 42;}
    int m_x;
};

int x;
const int cx = 42;
const int& crx = x;
const S* p = new S();

// x объявлен int: x_type есть int.
typedef decltype(x) x_type;

// auto также выводит int: a типа int.
auto a = x;

// cx объявлен const int: cx_type есть const int.
typedef decltype(cx) cx_type;

//auto удаляет квалификатор const: b типа int.
auto b = cx;

// crx объявлен как const int&: crx_type есть const int&.
typedef decltype(crx) crx_type;

// auto удаляет ссылочность и квалификатор const: c типа int.
auto c = crx;

// S::m_x объявлен int: m_x_type есть int
//
// Заметьте, что p->m_x нельзя присвоить значение. Это
// константа, поскольку p есть указатель на константу. Но decltype следует
// объявленному типу, который int.
typedef decltype(p->m_x) m_x_type;

// auto видит, что p->m_x константен, но он удаляет квалификатор const
// Следовательно, d типа int.
auto d = p->m_x;
 

Комментарии

Популярные сообщения из этого блога

Полезные новации С++17. Выражения свертки (Fold expressions)

Перегрузка и специализация шаблонных функций.

Ключевые слова auto и decltype – сходства, отличия и нюансы использования. Часть 3. decltype вывод типа сложного выражения.