I am wondering why the following code doesn't compile:
struct S
{
template <typename... T>
S(T..., int);
};
S c{0, 0};
This code fails to compile with both clang and GCC 4.8. Here is the error with clang:
test.cpp:7:3: error: no matching constructor for initialization of 'S'
S c{0, 0};
^~~~~~~
test.cpp:4:5: note: candidate constructor not viable: requires 1 argument, but 2 were provided
S(T..., int);
^
It seems to me that this should work, and T should be deduced to be a pack of length 1.
If the standards forbids doing things like this, does anyone know why?
Because when a function parameter pack is not the last parameter, then the template parameter pack cannot be deduced from it and it will be ignored by template argument deduction.
So the two arguments 0, 0 are compared against , int, yielding a mismatch.
Deduction rules like this need to cover many special cases (like what happens when two parameter packs appear next to each other). Since parameter packs are a new feature in C++11, the authors of the respective proposal drafted the rules conservatively.
Note that a trailing template parameter pack will be empty if it is not otherwise deduced. So when you call the constructor with one argument, things will work (notice the difference of template parameter pack and function parameter pack here. The former is trailing, the latter is not).
So, there should be a workaround. Something along these lines:
namespace v1 {
// Extract the last type in a parameter pack.
// 0, the empty pack has no last type (only called if 1 and 2+ don't match)
template<typename... Ts>
struct last_type {};
// 2+ in pack, recurse:
template<typename T0, typename T1, typename... Ts>
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...>{};
// Length 1, last type is only type:
template<typename T0>
struct last_type<T0> {
typedef T0 type;
};
}
namespace v2 {
template<class T> struct tag_t{using type=T;};
template<class T> using type_t = typename T::type;
template<class...Ts>
using last = type_t< std::tuple_element_t< sizeof...(Ts)-1, std::tuple<tag_t<Ts>...> > >;
template<class...Ts>
struct last_type {
using type=last<Ts...>;
};
}
template<class...Ts>
using last_type=v2::late_type<Ts...>; // or v1
struct S
{
// We accept any number of arguments
// So long as the type of the last argument is an int
// probably needs some std::decay to work right (ie, to implicitly work out that
// the last argument is an int, and not a const int& or whatever)
template <typename... T, typename=typename std::enable_if<std::is_same<int, typename last_type<T...>::type>>::type>
S(T...);
};
where we check that the last type of a parameter pack is an int, or that we where only passed an int.
I am actually a little interested in the same thing (wanting to specialize templated parameter packs based on the final arguments).
I believe there may be a path forward by combining tuple reversal (std::make_tuple, back-port std::apply for C++14, etc):
How to reverse the order of arguments of a variadic template function?
Will get back on here if it is successful.
Related posts:
Parameters after parameter pack in function
Parameter with non-deduced type after parameter pack
EDIT:
Yup, figured it out after a bit; not perfect, as there are extra copies flying around, but it's a start.
If you know a simpler way than what I list below, please don't hesitate to post!
TL;DR
Can do stuff like this:
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
And then implement this pseduo code:
template<typename ... Args>
void my_func(Args&& ... args, const my_special_types& x);
By doing something like:
template<typename... Args>
void my_func(Args&& ... args)
-> call my_func_reversed(args...)
template<typename... RevArgs>
void my_func_reversed(const my_special_types& x, RevArgs&&... revargs)
-> do separate things with revargs and my_special_types
-> sub_func_reversed(revargs...)
Using the above utilities.
Has some (a lot of) drawbacks. Will list them below.
Scope
This is for users of C++14 (maybe C++11), who want to borrow from the future (C++17).
Step 1: Reverse arguments
There are a few different ways to do this. I've listed out some alternatives in this example:
tuple.cc - Playground for two alternatives (credits in the source code):
Use foldable expressions and manipulate the index passed via std::apply_impl (credit: Orient).
Use recursive templates to construct a reversed index_sequence (credit: Xeo)
tuple.output.txt - Example output
This prints out the reversed_index_sequence template from Xeo's example. I needed this for debugging.
>>> name_trait<std::make_index_sequence<5>>::name()
std::index_sequence<0, 1, 2, 3, 4>
>>> name_trait<make_reversed_index_sequence<5>>::name()
std::index_sequence<4, 3, 2, 1, 0>
I chose Alternative 1, as it's easier for me to digest.
I then tried to formalize it right quick:
tuple_future.h - Borrowing from the future (namespace stdfuture), and making an extension (namespace stdcustom)
tuple_future_main.cc - Simple, advanced, and useful (see below) examples using the above
tuple_future_main.output.txt - Example output
Definition Snippets (adaptation of C++17 possible implementation of std::apply on cppreference.com):
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_reversed_impl(F &&f,
Tuple &&t, std::index_sequence<I...>)
{
// #ref https://stackoverflow.com/a/31044718/7829525
// Credit: Orient
constexpr std::size_t back_index = sizeof...(I) - 1;
return f(std::get<back_index - I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply_reversed(F &&f, Tuple &&t)
{
// Pass sequence by value to permit template inference
// to parse indices as parameter pack
return detail::apply_reversed_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
Usage Snippets: (from tuple_future_main.output.txt, copied from above)
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
Step 2: Buckle your shoe (with reversed parameter packs)
First, establish the patterns for the final arguments that you wish to use. You will have to explicitly enumerate these, as you can only have one parameter pack.
(Taken from tuple_future_main.cc):
Example Scenario:
We like to add things to containers with a name, something of the form:
add_item(const Item& item, const string& name, Container& c)
We can also construct an Item with a [awfully large] number of overloads, and
we have convenenience overloads:
add_item(${ITEM_CTOR_ARGS}, const string& name, Container& c)
To do so, we can declare the following:
void add_item_direct(const Item& item, const string& name, Container& c)
Item create_item(Args&&... args)
And then define our generic interfaces:
template<typename... Args>
void add_item(Args&&... args) {
...
auto reversed = stdcustom::make_callable_reversed(callable);
reversed(std::forward<Args>(args)...);
}
template<typename ... RevArgs>
void add_item_reversed(Container& c, const string& name, RevArgs&&... revargs)
{
...
static auto ctor = VARIADIC_CALLABLE(create_item,);
...
auto item = ctor_reversed(std::forward<RevArgs>(revargs)...);
add_item_direct(item, name, c);
}
Now we can do stuff like: (taken from tuple_future_main.output.txt)
>>> (add_item(Item("attribute", 12), "bob", c));
>>> (add_item("attribute", 12, "bob", c));
>>> (add_item(Item(2, 2.5, "twelve"), "george", c));
>>> (add_item(2, 2.5, "twelve", "george", c));
>>> (add_item(Item(2, 15.), "again", c));
>>> (add_item(2, 15., "again", c));
>>> c
bob - ctor3: ctor3: ctor1: attribute (12, 10)
bob - ctor3: ctor1: attribute (12, 10)
george - ctor3: ctor3: ctor2: 2, 2.5 (twelve)
george - ctor3: ctor2: 2, 2.5 (twelve)
again - ctor3: ctor3: ctor2: 2, 15 ()
again - ctor3: ctor2: 2, 15 ()
Note the extra copy constructors... :(
Drawbacks
Ugly as hell
May not be useful
It could be easier to just refactor your interfaces
However, this could be used as a stop-gap to transition to a more generalized interface.
Possibly fewer lines to delete.
Especially if it plugs your development process with template explosions
Can't nail down where the extra copies are coming from.
It may be due to judicious usage of variadic lambdas
You have to carefully craft your base functionality
You shouldn't try to extend an existing function.
Parameter packs will be greedy in how they match to functions
You either need to explicitly spell out each overload you want, or bow down and let the variadic parameter pack dispatch to your desired functionality
If you find an elegant way around this, please let me know.
Template errors are shitty.
Granted, not too shitty. But it's hard to infer that you missed an available overload.
Wraps a lot of simple functionality in lambdas
You may be able to use make_reversed_index_sequence and directly dispatch to the function (mentioned in other SO posts). But that's painful to repeat.
Todo
Get rid of extra copies
Minimize the need for all the lambdas
Not necessary if you have a Callable
Try to combat parameter pack greediness
Is there a generalized std::enable_if matching that matches to both lvalue- and rvalue-references, and possibly handle forwarding compatible implicit copy constructors?
template<typename ... Args>
void my_func(Args&& ... args) // Greedy
void my_func(magical_ref_match<string>::type, ...)
// If this could somehow automatically snatch `const T&` and `T&&` from the parameter pack...
// And if it can be used flexible with multiple arguments, combinatorically
Hopes
Maybe C++17 will support non-final parameter pack arguments, such that all of this can be discarded... fingers crossed
From the working draft of the standard N3376 § 14.1 is a probable section to read about this.
Below is § 14.1.11
If a template-parameter of a class template or alias template has a
default template-argument, each subsequent template-parameter shall
either have a default template-argument supplied or be a template
parameter pack. If a template-parameter of a primary class template or
alias template is a template parameter pack, it shall be the last
template-parameter. A template parameter pack of a function template
shall not be followed by another template parameter unless that
template parameter can be deduced from the parameter-type-list of the
function template or has a default argument.
Related
I am wondering why the following code doesn't compile:
struct S
{
template <typename... T>
S(T..., int);
};
S c{0, 0};
This code fails to compile with both clang and GCC 4.8. Here is the error with clang:
test.cpp:7:3: error: no matching constructor for initialization of 'S'
S c{0, 0};
^~~~~~~
test.cpp:4:5: note: candidate constructor not viable: requires 1 argument, but 2 were provided
S(T..., int);
^
It seems to me that this should work, and T should be deduced to be a pack of length 1.
If the standards forbids doing things like this, does anyone know why?
Because when a function parameter pack is not the last parameter, then the template parameter pack cannot be deduced from it and it will be ignored by template argument deduction.
So the two arguments 0, 0 are compared against , int, yielding a mismatch.
Deduction rules like this need to cover many special cases (like what happens when two parameter packs appear next to each other). Since parameter packs are a new feature in C++11, the authors of the respective proposal drafted the rules conservatively.
Note that a trailing template parameter pack will be empty if it is not otherwise deduced. So when you call the constructor with one argument, things will work (notice the difference of template parameter pack and function parameter pack here. The former is trailing, the latter is not).
So, there should be a workaround. Something along these lines:
namespace v1 {
// Extract the last type in a parameter pack.
// 0, the empty pack has no last type (only called if 1 and 2+ don't match)
template<typename... Ts>
struct last_type {};
// 2+ in pack, recurse:
template<typename T0, typename T1, typename... Ts>
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...>{};
// Length 1, last type is only type:
template<typename T0>
struct last_type<T0> {
typedef T0 type;
};
}
namespace v2 {
template<class T> struct tag_t{using type=T;};
template<class T> using type_t = typename T::type;
template<class...Ts>
using last = type_t< std::tuple_element_t< sizeof...(Ts)-1, std::tuple<tag_t<Ts>...> > >;
template<class...Ts>
struct last_type {
using type=last<Ts...>;
};
}
template<class...Ts>
using last_type=v2::late_type<Ts...>; // or v1
struct S
{
// We accept any number of arguments
// So long as the type of the last argument is an int
// probably needs some std::decay to work right (ie, to implicitly work out that
// the last argument is an int, and not a const int& or whatever)
template <typename... T, typename=typename std::enable_if<std::is_same<int, typename last_type<T...>::type>>::type>
S(T...);
};
where we check that the last type of a parameter pack is an int, or that we where only passed an int.
I am actually a little interested in the same thing (wanting to specialize templated parameter packs based on the final arguments).
I believe there may be a path forward by combining tuple reversal (std::make_tuple, back-port std::apply for C++14, etc):
How to reverse the order of arguments of a variadic template function?
Will get back on here if it is successful.
Related posts:
Parameters after parameter pack in function
Parameter with non-deduced type after parameter pack
EDIT:
Yup, figured it out after a bit; not perfect, as there are extra copies flying around, but it's a start.
If you know a simpler way than what I list below, please don't hesitate to post!
TL;DR
Can do stuff like this:
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
And then implement this pseduo code:
template<typename ... Args>
void my_func(Args&& ... args, const my_special_types& x);
By doing something like:
template<typename... Args>
void my_func(Args&& ... args)
-> call my_func_reversed(args...)
template<typename... RevArgs>
void my_func_reversed(const my_special_types& x, RevArgs&&... revargs)
-> do separate things with revargs and my_special_types
-> sub_func_reversed(revargs...)
Using the above utilities.
Has some (a lot of) drawbacks. Will list them below.
Scope
This is for users of C++14 (maybe C++11), who want to borrow from the future (C++17).
Step 1: Reverse arguments
There are a few different ways to do this. I've listed out some alternatives in this example:
tuple.cc - Playground for two alternatives (credits in the source code):
Use foldable expressions and manipulate the index passed via std::apply_impl (credit: Orient).
Use recursive templates to construct a reversed index_sequence (credit: Xeo)
tuple.output.txt - Example output
This prints out the reversed_index_sequence template from Xeo's example. I needed this for debugging.
>>> name_trait<std::make_index_sequence<5>>::name()
std::index_sequence<0, 1, 2, 3, 4>
>>> name_trait<make_reversed_index_sequence<5>>::name()
std::index_sequence<4, 3, 2, 1, 0>
I chose Alternative 1, as it's easier for me to digest.
I then tried to formalize it right quick:
tuple_future.h - Borrowing from the future (namespace stdfuture), and making an extension (namespace stdcustom)
tuple_future_main.cc - Simple, advanced, and useful (see below) examples using the above
tuple_future_main.output.txt - Example output
Definition Snippets (adaptation of C++17 possible implementation of std::apply on cppreference.com):
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_reversed_impl(F &&f,
Tuple &&t, std::index_sequence<I...>)
{
// #ref https://stackoverflow.com/a/31044718/7829525
// Credit: Orient
constexpr std::size_t back_index = sizeof...(I) - 1;
return f(std::get<back_index - I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply_reversed(F &&f, Tuple &&t)
{
// Pass sequence by value to permit template inference
// to parse indices as parameter pack
return detail::apply_reversed_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
Usage Snippets: (from tuple_future_main.output.txt, copied from above)
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
Step 2: Buckle your shoe (with reversed parameter packs)
First, establish the patterns for the final arguments that you wish to use. You will have to explicitly enumerate these, as you can only have one parameter pack.
(Taken from tuple_future_main.cc):
Example Scenario:
We like to add things to containers with a name, something of the form:
add_item(const Item& item, const string& name, Container& c)
We can also construct an Item with a [awfully large] number of overloads, and
we have convenenience overloads:
add_item(${ITEM_CTOR_ARGS}, const string& name, Container& c)
To do so, we can declare the following:
void add_item_direct(const Item& item, const string& name, Container& c)
Item create_item(Args&&... args)
And then define our generic interfaces:
template<typename... Args>
void add_item(Args&&... args) {
...
auto reversed = stdcustom::make_callable_reversed(callable);
reversed(std::forward<Args>(args)...);
}
template<typename ... RevArgs>
void add_item_reversed(Container& c, const string& name, RevArgs&&... revargs)
{
...
static auto ctor = VARIADIC_CALLABLE(create_item,);
...
auto item = ctor_reversed(std::forward<RevArgs>(revargs)...);
add_item_direct(item, name, c);
}
Now we can do stuff like: (taken from tuple_future_main.output.txt)
>>> (add_item(Item("attribute", 12), "bob", c));
>>> (add_item("attribute", 12, "bob", c));
>>> (add_item(Item(2, 2.5, "twelve"), "george", c));
>>> (add_item(2, 2.5, "twelve", "george", c));
>>> (add_item(Item(2, 15.), "again", c));
>>> (add_item(2, 15., "again", c));
>>> c
bob - ctor3: ctor3: ctor1: attribute (12, 10)
bob - ctor3: ctor1: attribute (12, 10)
george - ctor3: ctor3: ctor2: 2, 2.5 (twelve)
george - ctor3: ctor2: 2, 2.5 (twelve)
again - ctor3: ctor3: ctor2: 2, 15 ()
again - ctor3: ctor2: 2, 15 ()
Note the extra copy constructors... :(
Drawbacks
Ugly as hell
May not be useful
It could be easier to just refactor your interfaces
However, this could be used as a stop-gap to transition to a more generalized interface.
Possibly fewer lines to delete.
Especially if it plugs your development process with template explosions
Can't nail down where the extra copies are coming from.
It may be due to judicious usage of variadic lambdas
You have to carefully craft your base functionality
You shouldn't try to extend an existing function.
Parameter packs will be greedy in how they match to functions
You either need to explicitly spell out each overload you want, or bow down and let the variadic parameter pack dispatch to your desired functionality
If you find an elegant way around this, please let me know.
Template errors are shitty.
Granted, not too shitty. But it's hard to infer that you missed an available overload.
Wraps a lot of simple functionality in lambdas
You may be able to use make_reversed_index_sequence and directly dispatch to the function (mentioned in other SO posts). But that's painful to repeat.
Todo
Get rid of extra copies
Minimize the need for all the lambdas
Not necessary if you have a Callable
Try to combat parameter pack greediness
Is there a generalized std::enable_if matching that matches to both lvalue- and rvalue-references, and possibly handle forwarding compatible implicit copy constructors?
template<typename ... Args>
void my_func(Args&& ... args) // Greedy
void my_func(magical_ref_match<string>::type, ...)
// If this could somehow automatically snatch `const T&` and `T&&` from the parameter pack...
// And if it can be used flexible with multiple arguments, combinatorically
Hopes
Maybe C++17 will support non-final parameter pack arguments, such that all of this can be discarded... fingers crossed
From the working draft of the standard N3376 § 14.1 is a probable section to read about this.
Below is § 14.1.11
If a template-parameter of a class template or alias template has a
default template-argument, each subsequent template-parameter shall
either have a default template-argument supplied or be a template
parameter pack. If a template-parameter of a primary class template or
alias template is a template parameter pack, it shall be the last
template-parameter. A template parameter pack of a function template
shall not be followed by another template parameter unless that
template parameter can be deduced from the parameter-type-list of the
function template or has a default argument.
Is this not valid as C++14?
auto f = [](auto x, auto y = std::decay_t<decltype(x)>{}) { };
f(0);
I was expecting it to be roughly equivalent to
auto f = [](int x, int y) { };
f(0, int{});
Neither GCC 6.3 nor Clang 4.0 accepted my code.
http://ideone.com/b7b4SK GCC
http://ideone.com/EyLYaL Clang
Is it related to my lack of understanding of C++ template deduction phases? Does the 1400 pages long spec actually has an explicit answer to my question?
Update
To summarize, my problem can in fact be reduced to this piece of code (free of lambda, single parameter) and it is invalid under C++14 (thanks #BaummitAugen and #NirFriedman)
template <typename T>
void f(T x = 0) { }
int main() {
f();
}
The compilers are correct to reject your code, it is indeed not valid C++14.
In the standard (using N4141 here) we have
For a generic lambda, the closure type has a public inline function call
operator member template (14.5.2) whose template-parameter-list consists of one invented type template-
parameter for each occurrence of auto in the lambda’s parameter-declaration-clause, in order of appearance.
(5.1.2/4 [expr.prim.lambda]). So your call is equivalent to a call to some
template <class T1, class T2>
auto operator() (T1 x, T2 y = std::decay_t<decltype(x)>{});
Now
If a template parameter is used only in non-deduced
contexts and is not explicitly specified, template argument deduction fails.
(14.8.2/4 [temp.deduct.type]) and
The non-deduced contexts are:
[...]
- A template parameter used in the parameter type of a function parameter that has a default argument
that is being used in the call for which argument deduction is being done.
(14.8.2/5 [temp.deduct.type]) makes your call ill-formed.
I can't quote the spec, but I will quote cppreference which is an authoritative source and is often easier to read/follow. In particular, see http://en.cppreference.com/w/cpp/language/template_argument_deduction.
Non-deduced contexts
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction...
You probably are already aware that template parameters cannot always be deduced. Going down the list of entries, we see:
4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:
Which gives the following example:
template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = std::less<T>());
std::vector<std::string> v(3);
f(v);
variadic lambdas are basically equivalent to function templates with a type template parameter substituted for each usage of auto, so this example (which does not compile) is equivalent to your example.
So basically, the second type cannot be deduced because it is a non-deduced context.
This answer could probably be improved by giving a very good example of why exactly it's been decided to make this a non-deduced context, since naively it seems like it's possible. My guess is that this is basically because a function template is just that, a template for creating function. Defaulted arguments in turn, essentially create multiple callable signatures for the same function. So you can't really deal with defaulting, until you have a function, but you can't have a function until you instantiate, which requires knowing the template parameters.
It's instructive to note that this simpler example has the same issues:
template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = int{});
So the dependence on the first template parameter actually has nothing to do with the issue.
This question already has answers here:
What is the meaning of "... ..." token? i.e. double ellipsis operator on parameter pack
(2 answers)
Closed 8 years ago.
While looking at this question I found myself in the cpp reference site where I noticed a strange and new to me syntax :
template<class Ret, class... Args>
struct is_function<Ret(Args......)volatile &&> : std::true_type {};
Yep, 6 dots ! Initially I thought this was a typo, but after checking the libstdc++ source again there it was eg at line 444 :
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile &&> : public true_type { };
Is this a valid syntax ? Dot dot dot, are used to pack and unpack parameter packs ? What do 6 dots do ?
Why does libstdc++ use ... ... in it's implementation of is_function? If we check out the cppreference section for std::is_function it gives a sample implementation and says for the first ... ... case:
// specialization for variadic functions such as std::printf
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};
so we need the second set of ... to match a variadic function like printf:
Comma optional as per 8.3.5 [dcl.fct]
|
v
Ret(Args... ...)
^ ^
| |
Match a function with a variable number of arguments
|
and the function is a variadic function
Note, we have functions like fprintf that two arguments before the variadic terms and we need to match those as well.
Indeed if we use that implementation and attempt to match printf without the ... ... specialization then it fails see it live.
This corner of the language is covered in this post C++11's six dots:
I was mucking around the other day and discovered this nice little oddity:
template <typename... Args>
void foo(Args......);
As it turns out, ...... can be totally valid C++11. This is what happens when backward compatibility mixes with new hotness.
// These are all equivalent.
template <typename... Args> void foo1(Args......);
template <typename... Args> void foo2(Args... ...);
template <typename... Args> void foo3(Args..., ...);
Hopefully the last one shows what is happening here. [...]
Why is this valild? We can see that , ... is synonymous with ... from the draft C++11 standard section 8.3.5 [dcl.fct] which has the following grammar:
parameter-declaration-clause:
parameter-declaration-listopt...opt
parameter-declaration-list , ...
and says:
[...] Where syntactically correct and where “...” is not part of
an abstract-declarator, “, ...” is synonymous with “...”. [...]
In this case, the two are for different purposes. The first is for parameter pack expansion and the second is for variable argument lists. That particular declaration is to handle functions which take some regular parameters plus a variable argument list.
The difference is between run-time and compile-time variability. A function which takes a variable number of arguments at run-time is special. It is a single function which can handle a variable number of arguments from the caller:
void f(int x,...) // equivalent to void f(int x ...)
{
// Do some run-time logic here to determine what to
// do with parameters after x.
}
This is distinct from the notion that we want to be able to have a template which uses a variety of functions with various parameters which are known at compile time. For example, we could define a function template which takes a pointer to a function and allow the number and types of the arguments to vary:
template <typename... Args>
void g(void (*function_ptr)(Args...))
{
// We can do logic here to call function_ptr with the proper
// number of arguments.
}
Given these functions:
void f1(int);
void f2(int,float);
You can call g with any of them:
g(f1); // fine
g(f2); // also fine
However
g(f); // error
The compiler wouldn't know what to use for the Args parameter pack in g.
I'm following this great tutorial. The author heavily uses variadic templates and I came to a point where I'm stuck, can't understand. Can you help me?
1. Why isn't this compiling?
// this is simple
template<size_t I, typename T>
struct tuple_element
{
T value;
};
// this does NOT compiles: error: parameter pack 'Indices' must be at the end of the template parameter list
template <size_t... Indices, typename... Types>
struct tuple_impl : tuple_element<Indices, Types>...
{
};
Next, the author have this code that compiles fine:
template <size_t... Indices>
struct index_sequence
{
using type = index_sequence<Indices...>;
};
template <typename Sequence, typename... Types>
struct tuple_impl;
template <size_t... Indices, typename... Types>
struct tuple_impl<index_sequence<Indices...>, Types...>
: tuple_element<Indices, Types>...
{
};
2. Why in this case everything ok? I see almost the same pattern here: tuple_element<Indices, Types>...
3. Why this can't be compiled:
template <size_t... Indices>
void g(Indices...){}; //error: variable or field 'g' declared void
The error you are seeing is in the passing of parameters. This is a compile error:
template <size_t... Indices, typename... Types>
struct tuple_impl
{};
live example
The rule is you cannot have one pack followed by another in a template class template parameter list.
The second example is a specialization, where the pack rule does not exist. The template parameters of a specialization are merely the types and values which are extracted from the pattern matching against the primary templates, as guided by the <> portion of the specialization after the type name.
As they are never passed in in that same list & order, having one ... after another doesn't cause any ambiguity. With a primary template, the order matters, and anything after a ... is difficult to distinguish from more of the .... Probably to keep the compiler's job easier, even in cases where it cannot be ambiguous (say, a pack of literals followed by a pack of types), C++ bans its use.
The compiler would have no means to distinguish when the first sequence ends and the second starts - therefore it is allowed to have only one parameter pack in variadic templates, at the end.
tuple_impl<a, b, c, d, e, f> //is d still an index or already a type? what about e?
This works, because tuple_impl itself is a template that has itself only one parameter pack, the Types.... It just happes that in this specialization the first parameter is a template, too, which has a parameter pack, too. So, in contrast to one template with two packs, you have two templates with one pack each, which is ok.
This does not have to do with variadic templates, i.e. it would not work with a single argument either, for the same reason. The fact is, that since Indices... are not types but values, you are not defining a function. If it was not void, the compiler would have had problems later. Consider this example, which is slightly modified but essentially a similar construct:
template <size_t I>
unsigned g(I)
{}
The middle line is central: The compiler thinks that it is a variable definition, initialized with I. Therefore the error in your case, because variables of type void simply don't make sense. My compiler then emits a warning about the template, it thinks that g is a templated variable, and those are a C++1y extension. After that is done, he realizes the variable definition is not finished by a ;, emits an error and exits...
Just to add some important thing: this is a test snippet:
int main( int argc, char** argv )
{
using i3 = index_sequence<1, 2, 3>;
tuple_impl<i3, int, double, char> tup;
return 0;
}
Note: here you pass this i3 as the "index pack". The "master template" always defines how the parameters have to be passed to the template. The template<...> statement if set for specialization does not define anything, but just what internally the parameter combination may spread inside the specialization, but it's not a part of public interface.
For example, if you try to use <1, 2, 3, int, double, char> as a parameter specification (which would theoretically match the template parameter specification for the specialization), it will fail to compile.
This question already has answers here:
What is the meaning of "... ..." token? i.e. double ellipsis operator on parameter pack
(2 answers)
Closed 8 years ago.
While looking at this question I found myself in the cpp reference site where I noticed a strange and new to me syntax :
template<class Ret, class... Args>
struct is_function<Ret(Args......)volatile &&> : std::true_type {};
Yep, 6 dots ! Initially I thought this was a typo, but after checking the libstdc++ source again there it was eg at line 444 :
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile &&> : public true_type { };
Is this a valid syntax ? Dot dot dot, are used to pack and unpack parameter packs ? What do 6 dots do ?
Why does libstdc++ use ... ... in it's implementation of is_function? If we check out the cppreference section for std::is_function it gives a sample implementation and says for the first ... ... case:
// specialization for variadic functions such as std::printf
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};
so we need the second set of ... to match a variadic function like printf:
Comma optional as per 8.3.5 [dcl.fct]
|
v
Ret(Args... ...)
^ ^
| |
Match a function with a variable number of arguments
|
and the function is a variadic function
Note, we have functions like fprintf that two arguments before the variadic terms and we need to match those as well.
Indeed if we use that implementation and attempt to match printf without the ... ... specialization then it fails see it live.
This corner of the language is covered in this post C++11's six dots:
I was mucking around the other day and discovered this nice little oddity:
template <typename... Args>
void foo(Args......);
As it turns out, ...... can be totally valid C++11. This is what happens when backward compatibility mixes with new hotness.
// These are all equivalent.
template <typename... Args> void foo1(Args......);
template <typename... Args> void foo2(Args... ...);
template <typename... Args> void foo3(Args..., ...);
Hopefully the last one shows what is happening here. [...]
Why is this valild? We can see that , ... is synonymous with ... from the draft C++11 standard section 8.3.5 [dcl.fct] which has the following grammar:
parameter-declaration-clause:
parameter-declaration-listopt...opt
parameter-declaration-list , ...
and says:
[...] Where syntactically correct and where “...” is not part of
an abstract-declarator, “, ...” is synonymous with “...”. [...]
In this case, the two are for different purposes. The first is for parameter pack expansion and the second is for variable argument lists. That particular declaration is to handle functions which take some regular parameters plus a variable argument list.
The difference is between run-time and compile-time variability. A function which takes a variable number of arguments at run-time is special. It is a single function which can handle a variable number of arguments from the caller:
void f(int x,...) // equivalent to void f(int x ...)
{
// Do some run-time logic here to determine what to
// do with parameters after x.
}
This is distinct from the notion that we want to be able to have a template which uses a variety of functions with various parameters which are known at compile time. For example, we could define a function template which takes a pointer to a function and allow the number and types of the arguments to vary:
template <typename... Args>
void g(void (*function_ptr)(Args...))
{
// We can do logic here to call function_ptr with the proper
// number of arguments.
}
Given these functions:
void f1(int);
void f2(int,float);
You can call g with any of them:
g(f1); // fine
g(f2); // also fine
However
g(f); // error
The compiler wouldn't know what to use for the Args parameter pack in g.