Почему расширение пакета внутри неоцененного операнда приводит к последнему элементу?
Я могу сделать это внутри decltype()
:
auto g() -> decltype(1, "", true, new int);
Но не это:
template <class... Args>
auto g(Args&&... args) -> decltype(args...);
Сбой, потому что расширение пакета появляется внутри decltype()
но я думал, что расширение пакета приведет к списку аргументов через запятую. Так что возвращаемый тип g(a, b, c)
было бы decltype(c)
из-за того, как работает оператор запятой (он возвращает последний элемент). Это работает, когда вы расширяете список параметров функции, список параметров шаблона, список инициализатора и т. Д. Но почему это не так?
2 ответа
Пакеты параметров расширяются только при определенных обстоятельствах. Вы можете найти их в стандарте, выполнив поиск "расширение пакета". Например,
Пакет параметров функции представляет собой расширение пакета (14.5.3).
(8.3.5 / 14).
Если где-то явно не указано, что расширение пакета происходит в определенном контексте, оно не происходит и обычно запрещено грамматикой (то есть синтаксически некорректно). Например, decltype
требует выражения в качестве своего операнда. 1, "", true, new int
это действительно выражение (,
оператор запятой) но args...
это не выражение. Тем не мение, args...
является списком выражений, поэтому его можно использовать, например, при вызове функции.
Оператор запятой отличается от оператора выражения запятой.
Оператор запятой принимает два выражения, оценивает левую часть, отбрасывает его, вычисляет правую часть и возвращает результат.
Разделитель выражений используется, когда у вас есть список выражений, например, вызов функции или список инициализаторов.
decltype(a,b,c)
является decltype(
выражение )
не decltype(
список_выражений )
, Это означает, что ,
в вашем decltype
оператор запятой.
В общем, ...
расширение работает только тогда, когда грамматика допускает список выражений. ,
"сгенерированный" - это разделитель выражений, а не оператор запятой.
Я не знаю, как вы можете подражать поведению ,
оператор, в том числе порядок исполнения, используя ...
, Если вам все равно, в каком порядке они оцениваются, вы можете сделать:
template<class T, class... Ts>
struct last_type_helper{using type=T;};
template<class T0, class T1, class... Ts>
struct last_type_helper<T0, T1, Ts...>:last_type_helper<T1, Ts...>{}
template<class... Ts>
using last_type=typename last_type_helper<Ts...>::type;
template<class T0>
T0&& last_expression( T0&& t0 ) { return std::forward<T0>(t0); }
template<class T0, class...Ts>
auto last_expression( T0&& t0, Ts&&...ts )->last_type<T0, Ts...>&& {
return last_expression( std::forward<Ts>(ts)... );
}
затем
template<class...Args>
auto g(Args&&...args) -> decltype(last_expression(args...));
работает, как и
template<class...Args>
auto g(Args&&...args) -> last_type<Args...>;
который ставит телегу за лошадью, нет?