Ключевые слова auto и decltype – сходства, отличия и нюансы использования. Часть 2. decltype вывод типа простого выражения.
Проблема,
затронутая в конце предыдущего раздела до С++11 решалась некоторыми компиляторами
с помощью неофициального оператора
typeof.
template<typename
T, typename S>
void foo(T lhs, S rhs)
void foo(T lhs, S rhs)
{
typedef typeof(lhs * rhs) product_type;
}
typedef typeof(lhs * rhs) product_type;
}
Оператор
typeof
легализовался
как decltype. А вот другая
ситуация, когда этот оператор может
быть необходим — указание возвращаемого
типа в несколько измененном предыдущем
примере (не компилируется):
template<typename
T, typename S>
decltype(lhs * rhs) multiply(T lhs, S rhs)
decltype(lhs * rhs) multiply(T lhs, S rhs)
{
return lhs * rhs;
}
Скомпилировать
это не удастся, поскольку lhs
и
rhs использованы внутри decltype до их объявления как параметры функции. Чтобы
решить это затруднение, С++11 вводит новый
синтаксис вывода типа (trailing
return type):return lhs * rhs;
}
template<typename
T, typename S>
auto multiply(T lhs, S rhs) -> decltype(lhs * rhs)
auto multiply(T lhs, S rhs) -> decltype(lhs * rhs)
{
return 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;
};
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;
typedef decltype(x) x_type;
//
auto также выводит int: a типа int.
auto a = x;
auto a = x;
//
cx объявлен const int: cx_type есть const
int.
typedef decltype(cx) cx_type;
typedef decltype(cx) cx_type;
//auto
удаляет квалификатор const: b типа int.
auto b = cx;
auto b = cx;
//
crx объявлен как const int&: crx_type есть const
int&.
typedef decltype(crx) crx_type;
typedef decltype(crx) crx_type;
//
auto удаляет ссылочность и квалификатор
const: c типа int.
auto c = crx;
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;
//
// Заметьте, что 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;
// Следовательно, d типа int.
auto d = p->m_x;
Комментарии
Отправить комментарий