为什么在未评估的操作数内部的包扩展会导致最后一个元素?

时间:2014-10-31 19:16:45

标签: c++ templates c++11 variadic-templates comma-operator

我可以在decltype()

中执行此操作
auto g() -> decltype(1, "", true, new int);

但不是这样:

template <class... Args>
auto g(Args&&... args) -> decltype(args...);

它失败了,因为包{2}中出现了包扩展,但我认为包扩展会导致以逗号分隔的参数列表。因此,decltype()的返回类型将是g(a, b, c),因为逗号运算符的工作方式(它返回最后一个元素)。当您在函数参数列表,模板参数列表,初始化列表等内部展开时,它可以工作。但为什么这不是这种情况?

2 个答案:

答案 0 :(得分:5)

参数包仅在某些情况下展开。您可以通过搜索&#34; pack expansion&#34;在标准中找到它们。例如,

  

功能参数包是包扩展(14.5.3)。

(8.3.5 / 14)。

除非在特定上下文中明确指定包扩展,否则它不会发生,并且通常被语法禁止(,语法上不正确)。例如,decltype需要表达式作为其操作数。 1, "", true, new int确实是一个表达式(,是逗号运算符)但args...不是表达式。但是,args...表达式列表,因此可以在函数调用中使用例如

答案 1 :(得分:4)

逗号运算符与逗号表达式分隔符不同。

逗号运算符采用两个表达式,计算左侧,丢弃它,计算右侧,并返回结果。

当您有表达式列表(如函数调用或初始化列表)时,将使用表达式分隔符。

decltype(a,b,c)decltype(表达式),而不是decltype(表达式列表)。这意味着,中的decltype是操作员逗号。

通常,...扩展仅在语法允许表达式列表时有效。 ,“generated”是表达式分隔符,而不是逗号运算符。

我不知道您可以使用,模拟...运算符的行为,包括执行顺序。如果您不关心它们的评估顺序,您可以这样做:

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...>;

将车推到马后面,没有?