问题
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