C++11 variadic templates and comma-separated expressions equivalence

别说谁变了你拦得住时间么 提交于 2019-12-23 12:24:52

问题


In a variadic template the ... operator expands a parameter pack into a series of comma-separated arguments (in the simplest form). My question is: how come that calling some_function() for multiple arguments comma-separated works and calling it with the ... operator doesn't?

I'm talking about this code:

template<typename... Args> inline void expand(Args&&... args) 
{
   some_function(22),some_function(32); // Works
   some_function(args)...; // Doesn't work - ERROR
}

Shouldn't these two lines produce a similar output?


回答1:


Because in the first case you don't have comma-separated arguments but you are instead using the comma-operator, a totally different beast.

You can implement the function expand recursively:

inline void expand() {}

template<typename T, typename... Args>
inline void expand(T&& head, Args&&... tail)
{
    some_function(head);
    expand(tail...);
}



回答2:


As said in the other answer, the commas you get by expanding a parameter pack are not the comma oparator, but an argument list. Having an argument list as an expression is obviously an error. Since you don't need the return values of the function, you can try something in the lines of this:

template <class... T>
void ignore(T&&...) {}

template<typename... Args> inline void expand(Args&&... args) 
{
   ignore(some_function(args)...); 
}

if however some_function return void, the pack expansion won't work, since you cannot give void "values" to a function. You could either return a value or chain each call of some_function with a comma operator:

template<typename... Args> inline void expand(Args&&... args) 
{
   ignore( (some_function(args),true)...); 
   //or:
   bool b[] = {(some_function(args),true)...};
}



回答3:


A blunt answer is that this just isn't a context where standard allows pack expansion. The full list of allowed context is specified in 14.5.3/4:

4 A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:

— In a function parameter pack (8.3.5); the pattern is the parameter-declaration without the ellipsis.

— In a template parameter pack that is a pack expansion (14.1):

  • if the template parameter pack is a parameter-declaration; the pattern is the parameter-declaration without the ellipsis;

  • if the template parameter pack is a type-parameter with a template-parameter-list; the pattern is the corresponding type-parameter without the ellipsis.

— In an initializer-list (8.5); the pattern is an initializer-clause.

— In a base-specifier-list (Clause 10); the pattern is a base-specifier.

— In a mem-initializer-list (12.6.2); the pattern is a mem-initializer.

— In a template-argument-list (14.3); the pattern is a template-argument.

— In a dynamic-exception-specification (15.4); the pattern is a type-id.

— In an attribute-list (7.6.1); the pattern is an attribute.

— In an alignment-specifier (7.6.2); the pattern is the alignment-specifier without the ellipsis.

— In a capture-list (5.1.2); the pattern is a capture.

— In a sizeof... expression (5.3.3); the pattern is an identifier.

Here is one possible workaround that guarantees that arguments are evaluated in left-to-right order:

struct expand_aux {
    template<typename... Args> expand_aux(Args&&...) { }
};

template<typename... Args>
inline void expand(Args&&... args)
{
    expand_aux  temp { some_function(std::forward<Args>(args))...  };
}


来源:https://stackoverflow.com/questions/16011873/c11-variadic-templates-and-comma-separated-expressions-equivalence

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!