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?
So, there should be a workaround. Something along these lines:
where we check that the last type of a parameter pack is an
int
, or that we where only passed anint
.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).
From the working draft of the standard N3376 § 14.1 is a probable section to read about this.
Below is § 14.1.11
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-portstd::apply
for C++14, etc):Will get back on here if it is successful.
Related posts:
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:
And then implement this pseduo code:
By doing something like:
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:
std::apply_impl
(credit: Orient).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.I chose Alternative 1, as it's easier for me to digest. I then tried to formalize it right quick:
namespace stdfuture
), and making an extension (namespace stdcustom
)Definition Snippets (adaptation of C++17 possible implementation of
std::apply
on cppreference.com):Usage Snippets: (from
tuple_future_main.output.txt
, copied from above)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:
We can also construct an Item with a [awfully large] number of overloads, and we have convenenience overloads:
To do so, we can declare the following:
And then define our generic interfaces:
Now we can do stuff like: (taken from
tuple_future_main.output.txt
)Note the extra copy constructors... :(
Drawbacks
make_reversed_index_sequence
and directly dispatch to the function (mentioned in other SO posts). But that's painful to repeat.Todo
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?Hopes