#c #decltype
#c #decltype
Вопрос:
Я читаю книгу, в которой объясняются черты C , и есть пример из заголовка C type_traits со странным ?:
использованием, вот цитата из соответствующего файла /usr/include/c /…:
template<typename _Tp, typename _Up>
static __success_type<typename decay<decltype
(true ? std::declval<_Tp>()
: std::declval<_Up>())>::type> _S_test(int);
Оставляя в стороне цель данного объявления, ?:
использование оператора озадачивает меня в этом коде. Если первый операнд равен true
, то std::declval<_Tp>()
всегда будет выбираться в результате вычисления.
Как на самом деле работает этот выбор операнда declval?
Редактировать: первоначально прочитано в книге Николая М. Джосуттиса «Стандартная библиотека C : учебное пособие и справочник, 2-е изд.», стр.125. Но там он представлен в несколько упрощенной форме по сравнению с тем, что есть в моих файлах заголовков GCC.
Комментарии:
1. Кстати, что за книга?
2. Здесь важен тип выражения, а не то, какая часть будет вычисляться.
3. Что сказал Mat. Выражение используется для использования механизма вычета типов троичных условных операторов (вы можете прочитать их здесь ).
Ответ №1:
В выражении true ? std::declval<_Tp>() : std::declval<_Up>()
всегда выбирается первая альтернатива, но все выражение должно быть допустимым выражением. So std::declval<_Up>()
должно быть допустимым, и это означает _Up
, что должен быть вызываемый объект, который принимает нулевые аргументы. Кроме того, _Tp()
и _Up()
должен возвращать тот же тип (или один из типов должен быть неявно преобразован в другой), иначе троичный итератор не сможет выбрать возвращаемое значение.
Этот метод называется SFINAE (ошибка замены не является ошибкой). Идея заключается в том, что если создание экземпляра шаблона завершается неудачно, то это не ошибка, и этот шаблон просто игнорируется, а компилятор ищет другой.
Комментарии:
1. Они не должны возвращать точно такой же тип, если доступна неявная последовательность преобразования.
Ответ №2:
Идея здесь в том, что ?:
требуется, чтобы второй и третий операнды имели один и тот же тип, или один тип преобразуется в другой.
В противном случае создание экземпляра функции завершится неудачно, и будет выбрана какая-то другая перегрузка.