I have the following code:
#define RETURNS(...) -> decltype((__VA_ARGS__)) { return (__VA_ARGS__); }
template <typename This, typename... Args>
auto fun(This &&this_, Args&&... args) RETURNS(this_.fun(std::forward<Args>(args)...))
For better or worse, this allows me to use fun(o, args...) and o.f(args...) interchangeably. The difficulty in using this comes when using an initializer list as an argument. E.g.
fun(obj, {1, 2, 3}); // Where obj.fun eventually takes a std::vector<int>.
This fails due to a substitution error, so Clang says. Note, obj.fun({1, 2, 3});
works.
As I understand it from other questions, this is because initializer lists don't always play nicely with template argument deduction.
The closest I have to my desired syntax is by making the initializer list more explicit. To avoid verbosity, I have the following:
template <typename T> std::initializer_list<T> il(std::initializer_list<T> &&li) { return li; }
fun(obj, il({1, 2, 3}));
Is there a way of getting my desired syntax or closer to it?
Clang's error report in my test program is:
subst.cpp:16:5: error: no matching function for call to 'fun'
fun(x, {1, 2, 3});
^~~
subst.cpp:6:6: note: candidate template ignored: substitution failure [with This
= X &, Args = <>]: too few arguments to function call, single argument 'v'
was not specified
auto fun(This &&this_, Args&&... args) RETURNS(this_.fun(std::forward<Args>(args)...))
^ ~
You should have provided the signature of your member function fun
. Let's assume that it has the following signature:
class YourClassObj
{
public:
template<typename ...Args>
SomeType fun(Args ...args) {...}
...
};
Then the following call:
fun(obj, {1, 2, 3});
with a forwarding function, causes actually a non-deduced context related to failing to deduce type for {1,2,3}
. As per the Standard:
14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]
Template argument deduction is done by comparing each function template parameter type (call it
P
) with the type of the corresponding argument of the call (call itA
) as described below. IfP
is a dependent type, removing references and cv-qualifiers fromP
givesstd::initializer_list<P'>
orP'[N]
for someP'
andN
and the argument is a non-empty initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument, and in theP'[N]
case, ifN
is a non-type template parameter,N
is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5).
Examples from the standard:
template<class T> void f(std::initializer_list<T>); f({1,2,3}); // T deduced to int template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T
std::forward
essentially does template type deduction from function call. Scott Meyers describes in his Effective Modern C++ book this situation as one of the perfect forwarding failure cases. As he said in the book, one simple workaround is to use auto
:
auto __il = {1,2,3};
fun(obj, __il);
should compile fine, since auto
deduces {1,2,3}
to be a std::initializer_list<int>
. You can read the whole chapter for more detailed explanation.
And by the way, the other approach with a function il
returning a std::initializer_list<T>
should also compile, if the original fun
signature is actually the same as what I assumed (tested with gcc-4.9.1).