Using a variadic template parameter for vectors of tuples failing - c++

I am using g++ 5.2. Why does this work?
void func(std::vector<std::tuple<int>> v)
{
...
}
func({ std::make_tuple(1), std::make_tuple(2), std::make_tuple(3) });
But this does not? As they seem logically equivalent to me.
template <typename... Args>
void func(std::vector<std::tuple<Args...>> v)
{
...
}
func({ std::make_tuple(1), std::make_tuple(2), std::make_tuple(3) });
It gives the error:
error: could not convert ‘{std::make_tuple(_Elements&& ...) [with _Elements = {int}](), std::make_tuple(_Elements&& ...) [with _Elements = {int}](), std::make_tuple(_Elements&& ...) [with _Elements = {int}]()}’ from ‘<brace-enclosed initializer list>’ to ‘std::vector<std::tuple<>, std::allocator<std::tuple<> > >’
func({ std::make_tuple(1), std::make_tuple(2), std::make_tuple(3) });
^

They'd behave somewhat equivalently if you actually called them with a vector of tuples, but you don't do that.
void func(std::vector<std::tuple<int>> v)
This has an argument with a known type, and the initializer-list that you use can be converted to that type, so it works. You've said "the function takes type X, and here is an argument convertible to X". The compiler can do that.
template <typename... Args>
void func(std::vector<std::tuple<Args...>> v)
This function template has to deduce its argument type. But you don't call it with a vector of tuples that would allow it to deduce the tuple element types. You call it with an initializer-list that could be converted to lots of different types (vectors, lists, tuples, or many other types). The compiler can't deduce the type of the vector until it converts the initializer-list to that type, but it can't know if the conversion is valid until it knows what type of vector you have, so there's a chicken and egg situation. It can't deduce the type until it does the conversion, but can't do the conversion until it knows the type.

The compilation fails because function template arguments cannot be deduced from an initializer list.

Related

too many arguments to function std::make_shared<vector>

I am missing something with std::make_shared. Can't it resolve the type of a std::initializer_list, or am I doing something else wrong?
#include <vector>
#include <memory>
class A {};
int main()
{
A a;
std::vector<A> veca{A(), A{}, a}; // this works ofc
std::vector<A> vecb({A(), A{}, a}); // this too
std::make_shared<std::vector<A>>(vecb); // and this, ofc
std::make_shared<std::vector<A>>({a}); // what's wrong here?
return 0;
}
Error:
main.cpp:21:41: error: too many arguments to function ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = std::vector; _Args = {}]’
std::make_shared<std::vector<A>>({a});
^
In file included from /usr/include/c++/6/memory:82:0,
from main.cpp:10:
/usr/include/c++/6/bits/shared_ptr.h:632:5: note: declared here
make_shared(_Args&&... __args)
^~~~~~~~~~~
Live example: https://onlinegdb.com/r1DlHquDL
Consider the following minimal example of your problem:
template <typename... Ts>
void f(Ts&&...); // replacement for std::make_shared
int main()
{
f({1});
}
This case is described in the C++ Standard in [temp.deduct.call/1]:
Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives
std::initializer_list<P′> or P′[N] for some P′ and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list independently, taking P′ as separate function template parameter types P′i and the ith initializer element as the corresponding argument. In the P′[N] case, if N 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 ([temp.deduct.type]).
In your case, the last sentence applies. Interestingly, the error message says something else with GCC, which is weird. With Clang, the error message is clear:
error: no matching function for call to 'f'
note: candidate template ignored: substitution failure: deduced incomplete pack <(no value)> for template parameter 'Ts'

How to implicitly convert a pointer, to a const pointer, during variadic template deduction

Imagine the case, where one (for one reason, or another) wanted to implement a function, that acts like a proxy when calling functions. One could implement it as a template, which takes a pointer to a function, and all of its arguments. However, if there is a function, that accepts a const pointer to something (e.g. int const*), and you are trying to pass just a simple pointer to function (e.g. int*), the template deduction fails, due to ambiguous template argument. Example:
#include <utility>
template <class... TArgs>
void foo (void (*pFunc)(TArgs...), TArgs... args)
{
pFunc (std::forward<TArgs> (args)...);
}
void bar (int const* pInt) {}
int main ()
{
int a = 5;
foo (bar, &a);
}
Produces:
error C2672: 'foo': no matching overloaded function found
error C2782: 'void foo(void (__cdecl *)(TArgs...),TArgs...)': template parameter 'TArgs' is ambiguous
note: see declaration of 'foo'
note: could be 'const int*'
note: or 'int*'
Even if, simply calling bar (&a) would succeed due to implicit conversion between int*, and int const*.
Yes, I realize, that one could specify the argument types directly (e.g. (foo<int const*> (bar, &a)), but given the fact, that arbitrary amount of arguments are accepted, such list would be quite long, and, would, in my personal opinion, look ugly.
Another option, would be, to liter the code with const_casts, wherever such conversion would be needed, but that would, also, inflate the code in undesirable ways (or in other words, would look ugly, as well).
The 3rd solution, that I could think of, would be to provide 2 parameter packs, like so:
template <class... TArgs, class... TArgs2>
void foo (void (*pFunc)(TArgs...), TArgs2... args)
{
pFunc (std::forward<TArgs2> (args)...);
}
Which, would solve the immediate problem of implicit argument conversion, but would introduce another problem, of compiler errors pointing to the foo implementation, instead of foo invocation, in the case of mismatched arguments to the function, which would make it harder to identify where exactly, in the code base, the mismatched call was being made. Example of errors, that I get, if I use the above example, and the following main function:
int main ()
{
float b = 5;
foo (bar, &b);
}
error C2664: 'void (const int *)': cannot convert argument 1 from 'float *' to 'const int *'
note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
note: see reference to function template instantiation 'void foo<const int*,float*>(void (__cdecl *)(const int *),float *)' being compiled
So, the question is, is there some sort of template magic, that would allow it to work in the manner, that I expect, given the restrictions, and considerations, I have given above? Or am I just being unreasonable with my requirements?
You could constrain the function:
template <class... TArgs, class... TArgs2>
enable_if_t<is_invocable_v<void(TArgs...),TArgs2...>>
foo (void (*pFunc)(TArgs...), TArgs2... args)
{
pFunc (forward<TArgs2> (args)...);
}

Could not convert from brace-enclosed initializer list to std tuple

As part of a bigger project, I'm playing with std::tuple and templates; consider the following code:
template <typename ...T> void foo(tuple<T...> t) {}
void bar(tuple<int, char> t) {}
tuple<int, char> quxx() { return {1, 'S'}; }
int main(int argc, char const *argv[])
{
foo({1, 'S'}); // error
foo(make_tuple(1, 'S')); // ok
bar({1, 'S'}); // ok
quxx(); // ok
return 0;
}
According to this answer C++17 supports tuple initialization from copy-list-initialization, however it seems such support is limited since I get the following error (GCC 7.2.0):
main.cpp: In function 'int main(int, const char**)':
main.cpp:14:17: error: could not convert '{1, 'S'}' from '<brace-enclosed initializer list>' to 'std::tuple<>'
foo({1, 'S'}); // error
^
Is there any way I can use brace-enclosed syntax in this scenario?
Some Context : this is going to be used in an operator overload so I guess I'm bound to tuples and cannot make use of variadics, any hint is well-accepted.
Extra : Clang 6 also complains
prog.cc:12:5: error: no matching function for call to 'foo'
foo({1, 'S'}); // error
^~~
prog.cc:6:31: note: candidate function [with T = <>] not viable: cannot convert initializer list argument to 'tuple<>'
template <typename ...T> void foo(tuple<T...> t) {}
A braced-init-list, like {1, 'S'}, does not actually have a type. In the context of template deduction, you can only use them in certain cases - when deducing against initializer_list<T> (where T is a function template parameter) or when the corresponding parameter is already deduced by something else. In this case, neither of those two things is true - so the compiler cannot figure out what ...T is supposed to be.
So you can provide the types directly:
foo<int, char>({1, 'S'});
Or you can construct the tuple yourself and pass that in:
foo(std::tuple<int, char>(1, 'S')); // most explicit
foo(std::tuple(1, 'S')); // via class template argument deduction
Today, ClassTemplate<Ts...> can only be deduced from expressions of type ClassTemplate<Us...> or types that inherit from something like that. A hypothetical proposal could extend that to additionally try to perform class template argument deduction on the expression to see if that deduction succeeds. In this case, {1, 'S'} isn't a tuple<Ts...> but tuple __var{1, 'S'} does successfully deduce tuple<int, char> so that would work. Such a proposal would also have to address issues like... what if we're deducing ClassTemplate<T, Ts...> or any minor variation, which isn't something that class template argument deduction allows (but is something that many people have at times expressed interest in being able to do).
I'm not aware of such a proposal today.
According to this answer C++17 supports tuple initialization from copy-list-initialization, however it seems such support is limited since I get the following error
The problem is another.
When you call bar({1, 'S'}), the compiler knows that bar() receive a tuple<int, char>, so take 1 as int and 'S' as char.
See another example: if you define
void baz (std::tuple<int> const &)
{ }
you can call
baz(1);
because the compiler knows that baz() receive a std::tuple<int> so take 1 to initialize the int in the tuple.
But with
template <typename ...T>
void foo(tuple<T...> t)
{ }
the compiler doesn't know the T... types; when you call
foo({1, 'S'});
what T... types should deduce the compiler?
I see, at least, two hypothesis: T = int, char or T = std::pair<int, char>; or also T = std::tuple<int, char>.
Which hypothesis should follows the compiler?
I mean: if you pass a std::tuple to foo(), the compiler accept the list of types in the tuple as the list of T...; but if you pass something else, the compiler must deduce the correct std::tuple; but this deduction, in this case, is not unique. So the error.

Member function pointer issue with standard library methods

This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.
The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.

Passing a member function pointer to an overloaded class method into a template function [duplicate]

This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.
The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.