Reputation: 1863
I am not able to pass an initializer list to a templated function, to then call a non templated function which would coerce the initializer list to a container. However, when skipping the intermediate templated function, it compiles.
#include <iostream>
#include <vector>
struct MyLoader {
static auto load(const std::vector<int> &v) {
return v;
}
};
template<typename Loader, typename... Args>
auto makeVector(Args&&... args) {
return Loader::load(std::forward<Args>(args)...);
}
int main() {
auto v = makeVector<MyLoader>({ 8, 8, 8 });
for (auto x : v) {
std::cout << x << "\n";
}
}
Renders the error:
main.cpp: In instantiation of 'auto makeVector(Args&& ...) [with Loader = MyLoader; Args = {}]':
main.cpp:18:46: required from here
main.cpp:13:24: error: no matching function for call to 'MyLoader::load()'
return Loader::load(std::forward<Args>(args)...);
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:6:17: note: candidate: 'static auto MyLoader::load(const std::vector<int>&)'
static auto load(const std::vector<int> &v) {
^~~~
main.cpp:6:17: note: candidate expects 1 argument, 0 provided
main.cpp: In function 'int main()':
main.cpp:18:46: error: too many arguments to function 'auto makeVector(Args&& ...) [with Loader = MyLoader; Args = {}]'
auto v = makeVector<MyLoader>({ 8, 8, 8 });
^
main.cpp:12:6: note: declared here
auto makeVector(Args&&... args) {
^~~~~~~~~~
main.cpp:18:10: error: 'void v' has incomplete type
auto v = makeVector<MyLoader>({ 8, 8, 8 });
^
main.cpp:20:19: error: unable to deduce 'auto&&' from 'v'
for (auto x : v) {
However the following compiles perfectly fine:
#include <iostream>
#include <vector>
struct MyLoader {
static auto load(const std::vector<int> &v) {
return v;
}
};
int main() {
auto v = MyLoader::load({ 8, 8, 8 });
for (auto x : v) {
std::cout << x << "\n";
}
}
Why is the initializer list not coerced to the std::vector
argument of MyLoader::load()
in the first example?
Upvotes: 1
Views: 77
Reputation: 66230
The problem is that {8, 8, 8}
isn't deducible as type.
If you pass it to a method that receive a const std::vector<int>
, all goes well because the compiler know that {8, 8, 8}
is used to initialize a std::vector<int>
.
But if you pass it to a function that receive a variadic list (Args&&... args
) of unknown types arguments, this doesn't works because the compiler doesn't know witch type is {8, 8, 8}
.
Suggestion: pass the values in a variadic way
auto v = makeVector<MyLoader>(8, 8, 8);
so the compiler can detect Args...
as int, int, int
, and pass to load()
adding graphs
// ------------------V-----------------------------V
return Loader::load( { std::forward<Args>(args)... } );
If you really (really!) want to pass th values to makeVector()
as initialization list, you can intercept they as an array
template <typename Ldr, std::size_t Dim, typename Arg>
auto makeVector (Arg(&&arg)[Dim])
So the compiler can deduce the type (Arg
, as int
) and the size (Dim
, 3
in case of {8, 8, 8}
.
But to unpack the array you need the indexes, so a function helper.
Something as
template <typename Ldr, std::size_t ... Is, std::size_t Dim, typename Arg>
auto makeVectorH (std::index_sequence<Is...> const &, Arg(&&arg)[Dim])
{ return Ldr::load({std::forward<Arg>(arg[Is])...}); }
template <typename Ldr, std::size_t Dim, typename Arg>
auto makeVector (Arg(&&arg)[Dim])
{ return makeVectorH<Ldr>(std::make_index_sequence<Dim>{},
std::forward<Arg[Dim]>(arg)); }
The following is a full compiling C++14 example for array case (but I suggest the simpler use of variadic arguments)
#include <vector>
#include <utility>
#include <iostream>
struct MyLoader
{ static auto load(const std::vector<int> &v) { return v; } };
template <typename Ldr, std::size_t ... Is, std::size_t Dim, typename Arg>
auto makeVectorH (std::index_sequence<Is...> const &, Arg(&&arg)[Dim])
{ return Ldr::load({std::forward<Arg>(arg[Is])...}); }
template <typename Ldr, std::size_t Dim, typename Arg>
auto makeVector (Arg(&&arg)[Dim])
{ return makeVectorH<Ldr>(std::make_index_sequence<Dim>{},
std::forward<Arg[Dim]>(arg)); }
int main ()
{
auto v = makeVector<MyLoader>({8, 8, 8});
for (auto x : v)
std::cout << x << "\n";
}
Upvotes: 1