Implicit conversion of initializer lists and perfect forwarding - c++

I'm trying to make perfect forwarding work with initializer lists. For the sake of the example, I'd like to have a variadic function that calls into another function, and still enjoy automatic conversion of initializer lists of the latter:
#include <iostream>
#include <vector>
void hello(std::string const& text, std::vector<int> const& test)
{
std::cout << "hello " << text << " " << test.size() << std::endl;
}
template<class ... Args>
void f(Args&& ... args)
{
return hello(std::forward<Args>(args)...);
}
int main()
{
hello("world", {1,2,3}); // WORKS
f("world", std::vector<int>({1,2,3})); // WORKS
f("world", {1,2,3}); // COMPILER ERROR
}
The error is
example.cpp: In function ‘int main()’:
example.cpp:21:21: error: too many arguments to function ‘void f(Args&& ...) [with Args = {}]’
21 | f("world", {1,2,3});
| ^
example.cpp:12:6: note: declared here
12 | void f(Args&& ... args)
| ^
example.cpp: In instantiation of ‘void f(Args&& ...) [with Args = {}]’:
example.cpp:21:21: required from here
example.cpp:14:15: error: too few arguments to function ‘void hello(const string&, const std::vector<int>&)’
14 | return hello(std::forward<Args>(args)...);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
example.cpp:6:6: note: declared here
6 | void hello(std::string const& text, std::vector<int> const& test)
| ^~~~~
Am I making any obvious mistake here?

The compiler is not able to recognize the type you are sending in the third case.
If you use
f("world", std::initializer_list<int>{1,2,3});
everything works.
This post has some detailed explanation and quotes the relevant part of the standard. It is for a slightly different case but the explanation still applies.

The problem is that the {1, 2, 3} argument to your second call to the templated f function is not sufficiently 'specific' for the compiler to unambiguously deduce its type in template substitution.
Explicitly defining that argument's type will resolve the issue:
f("world", std::initializer_list<int>{ 1, 2, 3 });
A very similar case is given (as an example of an error) on this cppreference page.

Related

Type deduction resullts in ambiguous call of overloaded function

While mixing type deduction with overloading, I stumbled upon a behavior of type deduction for lambda functions that I find difficult to understand.
When compiling this program:
#include <functional>
#include <cstdlib>
int case_0(int const& x) {
return 2*x;
}
int case_1(int& x) {
x += 2;
return 2*x;
}
class Test {
public:
Test(int const n) : n(n) {}
int apply_and_increment(std::function<int(int const&)> f) {
n++;
return f(n);
}
int apply_and_increment(std::function<int(int&)> f) {
return f(n);
}
private:
int n;
};
int main() {
Test t(1);
auto f = [](int const& x) -> int {
return 3*x;
};
t.apply_and_increment(case_0); // Fails compilation
t.apply_and_increment(std::function<int(int const&)>(case_0)); // Succeeds
t.apply_and_increment(case_1); // Succeeds
t.apply_and_increment(f); // Fails compilation
return EXIT_SUCCESS;
}
The output of the compilation is:
$ g++ -std=c++20 different_coonstness.cpp -o test
different_coonstness.cpp: In function ‘int main()’:
different_coonstness.cpp:34:30: error: call of overloaded ‘apply_and_increment(int (&)(const int&))’ is ambiguous
34 | t.apply_and_increment(case_0);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:37:25: error: call of overloaded ‘apply_and_increment(main()::<lambda(const int&)>&)’ is ambiguous
37 | t.apply_and_increment(f);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
As far as I understand:
case_0 is ambiguous because there are 2 valid type conversions, std::function<int(const int&)> and std::function<int(int&)>, and both overloaded functions apply_and_increment() can be applied. This is why the explicit type conversion std::function<int(int const&)>(case_0) is required.
in case_1, the only valid conversion is std::function<int(int&)>, so there is no ambiguity.
I am not very familiar with type deduction and lambdas, so I am a bit surprised that t.apply_and_increment(f) fails to compile. I would expect that the type of the function would be deduced by the type signature, [](int const& x) -> int, in the lambda function.
Why is not f of type std::function<int(int const&)>?
Your understanding of overload resolution for case_0 and case_1 is correct:
A reference-to-non-const can be assigned to a reference-to-const, hence case_0() is callable from both of the std::function types being used, thus overload resolution is ambiguous when an implicit conversion is used, requiring you to specify the desired std::function type explicitly.
A reference-to-const cannot be assigned to a reference-to-non-const, hence case_1() is not callable from std::function<int(int const&)>, only from std::function<int(int&)>, thus overload resolution is not ambiguous when an implicit conversion is used.
A standalone function is not itself a std::function object, but can be assigned to a compatible std::function object.
Likewise, a lambda is not itself a std::function object, it is an instance of a compiler-defined functor type, which can be assigned to a compatible std::function object.
In both cases, std::function acts as a proxy, passing its own parameters to the function/lambda's parameters, and then returning whatever the function/lambda returns.
So, overload resolution fails for both case_0 and f for the exact same reason. When the compiler has to implicitly convert case_0/f into a std::function object, the conversion is ambiguous because case_0/f is callable from both of the std::function types being used.

Passing overloaded function and args to template function

I'd like to send the two overloaded fun()s to one template that handles them plus their arguments. This is my attempt so far:
#include <vector>
#include <iostream>
using namespace std;
class Demo
{};
template<typename Function, typename... Args>
void call(Function func(Args...), Args &&...args)
{
func(forward<Args>(args)...); // execute function with args
}
void fun(int first, int second, int third)
{
cout << "fun with ints\n";
}
void fun(Demo &&dem1, Demo &&dem2) // adding overload causes the ambiguity
{
cout << "fun with Demos\n";
}
int main()
{
call(fun, 1, 2, 3);
call(fun, Demo{}, Demo{});
}
The compiler complains that it can't find a matching function for the calls in main():
main.cc: In function ‘int main()’:
main.cc:27:22: error: no matching function for call to ‘call(<unresolved overloaded function type>, int, int, int)’
27 | call(fun, 1, 2, 3);
| ^
main.cc:10:6: note: candidate: ‘template<class Function, class ... Args> void call(Function (*)(Args ...), Args&& ...)’
10 | void call(Function func(Args...), Args &&...args)
| ^~~~
main.cc:10:6: note: template argument deduction/substitution failed:
main.cc:27:22: note: couldn’t deduce template parameter ‘Function’
27 | call(fun, 1, 2, 3);
| ^
main.cc:29:29: error: no matching function for call to ‘call(<unresolved overloaded function type>, Demo, Demo)’
29 | call(fun, Demo{}, Demo{});
| ^
main.cc:10:6: note: candidate: ‘template<class Function, class ... Args> void call(Function (*)(Args ...), Args&& ...)’
10 | void call(Function func(Args...), Args &&...args)
| ^~~~
main.cc:10:6: note: template argument deduction/substitution failed:
main.cc:29:29: note: couldn’t deduce template parameter ‘Function’
29 | call(fun, Demo{}, Demo{});
|
Any ideas to find a solution to this puzzle would be highly appreciated!
(the solution at C++ overloaded function as template argument did not solve my problem because I cannot change the way that call() is called in main() )
The reason for compilation error is that the compiler does not know which fun overload you are actually going to use.
To resolve this error, you just need to cast your function parameter to the right overload like:
int main()
{
call( static_cast< void(*)(int, int, int) >( fun ), 1, 2, 3 );
call( static_cast< void(*)(Demo&&, Demo&&) >( fun ), Demo{}, Demo{} );
return 0;
}
FYI, what your call function is trying to do is actually defined by the standard. It is std::invoke function and it comes with C++17 standard.

C++ 20 Concepts: Require operator overloading

I am trying to understand how to declare a concept that requires a particular operator is overloaded for a given type. Lets say I have the following function that takes a vector of an arbitrary type and prints it to std::cout:
template<typename printable>
void print_vector(const std::vector<printable>& vec)
{
std::cout << '{';
for (const printable &item : vec) {
std::cout << item << ',';
}
std::cout << '}';
}
This code will work just fine if the type printable has an overloaded << operator, but if it doesn't, then it fails with a very unhelpful compiler error. I feel like I should be able to somehow declare a concept that requires a type has a valid << operator defined, and use that concept in the function declaration, so that I can get a more useful compiler error, but I haven't been able to figure out how to do it.
template <class T>
concept Printable = requires(std::ostream& os, T a)
{
os << a;
};
template<Printable T>
void print_vector(const std::vector<T>& vec) {
std::cout << '{';
for (const auto &item : vec) {
std::cout << item << ',';
}
std::cout << '}';
}
If you wish you could also make it more generic to operate on basic_ostream.
Here is the clang error message:
<source>:30:5: error: no matching function for call to 'print_vector'
print_vector(x);
^~~~~~~~~~~~
<source>:19:6: note: candidate template ignored: constraints not satisfied [with T = X]
void print_vector(std::vector<T> vec) {
^
<source>:18:10: note: because 'X' does not satisfy 'Printable'
template<Printable T>
^
<source>:10:9: note: because 'os << a' would be invalid: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'X')
os << a;
^
and gcc:
<source>: In function 'auto test()':
<source>:30:19: error: use of function 'void print_vector(std::vector<T>) [with T = X]' with unsatisfied constraints
30 | print_vector(x);
| ^
<source>:19:6: note: declared here
19 | void print_vector(std::vector<T> vec) {
| ^~~~~~~~~~~~
<source>:19:6: note: constraints not satisfied
<source>: In instantiation of 'void print_vector(std::vector<T>) [with T = X]':
<source>:30:19: required from here
<source>:8:9: required for the satisfaction of 'Printable<T>' [with T = X]
<source>:8:21: in requirements with 'std::ostream& os', 'T a' [with T = X]
<source>:10:9: note: the required expression '(os << a)' is invalid
10 | os << a;
| ~~~^~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail

Storing the result of a bind with placeholders in a std::function

I have been reading up on, how to perform a std::bind on a regular function.
And store the free function or member function into a std::function.
However, if I try to use a placeholder for one argument and an actual value for the other argument; I am not able to make a call(causes compilation error) to the std::function
So I tried the following code:
#include <random>
#include <iostream>
#include <memory>
#include <functional>
int g(int n1, int n2)
{
return n1+n2;
}
int main()
{
using namespace std::placeholders; // for _1, _2, _3...
std::function<int(int,int)> f3 = std::bind(&g, std::placeholders::_1, 4);
std::cout << f3(1) << '\n';
//this works just fine
auto f4 = std::bind(&g, std::placeholders::_1, 4);
std::cout << f4(1) << '\n';
}
I get the following error g++ 4.7
prog.cpp: In function 'int main()':
prog.cpp:17:22: error: no match for call to '(std::function<int(int, int)>) (int)'
std::cout << f3(1) << '\n';
^
In file included from /usr/include/c++/4.9/memory:79:0,
from prog.cpp:3:
/usr/include/c++/4.9/functional:2142:11: note: candidate is:
class function<_Res(_ArgTypes...)>
^
/usr/include/c++/4.9/functional:2434:5: note: _Res std::function<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = int; _ArgTypes = {int, int}]
function<_Res(_ArgTypes...)>::
^
/usr/include/c++/4.9/functional:2434:5: note: candidate expects 2 arguments, 1 provided
If you're binding an argument to the function int g(int, int), what remains as a callable is a function taking one int as an argument, not two.
Try this:
std::function<int(int)> f3 = std::bind(&g, std::placeholders::_1, 4);
the type of your std::function should be:
std::function<int(int)> f3 = std::bind(&g, std::placeholders::_1, 4);
~~~
one argument
Your bind creates a function with one parameter. That's why you call f3 as this:
std::cout << f3(1) << '\n';
note: candidate expects 2 arguments, 1 provided
should have been your clue

no viable overloaded '=' for overloaded static member functions

I have this simplified code consisting of a class with a static function, which is stored in map:
#include <iostream>
#include <functional>
#include <map>
class A {
public:
static void f(const std::string &s) { std::cout << s; }
};
std::map<std::string, std::function<void(std::string const &)>> fs;
int main() {
fs["f"] = &A::f;
fs["f"]("hello");
}
This prints the expected hello.
The problem occurs if I overload f() with:
static void f(const std::string &s, int c) { while(c-->0) { std::cout << s; } }
This results in the error:
error: no viable overloaded '='
fs["f"] = &A::f;
~~~~~~~ ^ ~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2241:7: note: candidate function not viable: no overload of 'f' matching 'const std::function<void (const std::basic_string<char> &)>' for 1st argument
operator=(const function& __x)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2259:7: note: candidate function not viable: no overload of 'f' matching 'std::function<void (const std::basic_string<char> &)>' for 1st argument
operator=(function&& __x)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2273:7: note: candidate function not viable: no overload of 'f' matching 'nullptr_t' for 1st argument
operator=(nullptr_t)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2302:2: note: candidate template ignored: couldn't infer template argument '_Functor'
operator=(_Functor&& __f)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/functional:2311:2: note: candidate template ignored: couldn't infer template argument '_Functor'
operator=(reference_wrapper<_Functor> __f) noexcept
^
However, calling both functions works:
A::f("hello ");
A::f("echo ", 3);
So, my question are:
Why this code not compiling even though the operator= seems to exist and function if I don't overload f()?
How can I get it to work without giving both functions different names?
Why this code not compiling even though the operator= seems to exist
and function if I don't overload f()?
Because the compiler doesn't know which overload to choose. How could he? There is no criterion upon which he can decide which one is suited better. Every std::function allows arbitrary function objects to be assigned and doesn't check any signatures. If you wanted to save only function pointers of this particular signature you should have declared the map appropriately.
How can I get it to work without giving both functions different
names?
As already mentioned it works by casting the expression to a function pointer of the specific type.
fs["f"] = static_cast<void(*)(std::string const&)>( &A::f );
This way no ambiguities arise; There is exactly one overload that can be casted to this function to pointer type. If this appears more often then a typedef could be feasible.
Or a little helper class template:
template <typename... Exact>
struct funptr
{
template <typename R>
constexpr auto operator()(R(*p)(Exact...)) -> decltype(p)
{ return p; }
};
fs["f"] = funptr<std::string const&>()(&A::f);
Demo.