Cannot capture reference in lambda from universal reference? - c++

I am trying to figure out why the captured variable cannot be passed to the foo function by reference, the program shown below does not compile with the following error:
error: invalid initialization of reference of type
'Object&' from expression of type 'const Object'
Is there some way to perfect forward the universal reference into the lambda so that it is captured by reference for later use? Types T might also be R-Values or a combination of R-Values and L-Values, so they cannot always be captured by reference.
struct Object
{
};
void foo(Object& a)
{
}
template<typename... T>
auto bar(T&&... values)
{
return [values...](){
foo(values...);
};
}
int main ()
{
Object obj;
auto lambda = bar(obj);
return 0;
}

You're by capturing by values which stores values as const members inside the closure, and they cannot bind to non-const lvalue references. For capture by-ref you've forgot to put '&' before values:
return [&values...](){
foo(values...);
};
A small example.
The only diff between the two files:
diff omg.cpp nyan.cpp
6c6
< return [values...]{ foo(values...); };
---
> return [&values...]{ foo(values...); };
Compile the one with by-value capture:
$ g++ -std=c++14 omg.cpp && ./a.out
omg.cpp: In instantiation of ‘bar(T&& ...)::<lambda()> [with T = {int&}]’:
omg.cpp:6:16: required from ‘struct bar(T&& ...) [with T = {int&}]::<lambda()>’
omg.cpp:6:38: required from ‘auto bar(T&& ...) [with T = {int&}]’
omg.cpp:11:16: required from here
omg.cpp:6:25: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
return [values...]{ foo(values...); };
~~~^~~~~~~~~~~
omg.cpp:3:6: note: initializing argument 1 of ‘void foo(int&)’
void foo(int &a) { std::cout << a << std::endl; }
^~~
Compile the other:
$ g++ -std=c++14 nyan.cpp && ./a.out
42
314

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.

Allocator-related error when passing lvalue to a universal reference parameter that is used as template parameter to a temporary std container

I have stripped down my use case to a minimal meaningless example:
#include <vector>
template<typename Container>
void f(Container&) { // using && gives compilation error... (*)
std::vector<Container>{};
};
int main() {
std::vector<int> v;
f(v); // (*) ... which is "solved" by std::move-ing v here. Why?
}
which compiles. However, when I add another & to make the unnamed parameter a forwarding reference, I get the error below (compiling with g++ -std=c++11 thatfile.cpp or -std=c++17). On the other hand, std::moveing v at the call site, solves the problem.
I'd like to understand why.
In file included from /usr/include/c++/10.2.0/x86_64-pc-linux-gnu/bits/c++allocator.h:33,
from /usr/include/c++/10.2.0/bits/allocator.h:46,
from /usr/include/c++/10.2.0/vector:64,
from prova.cpp:1:
/usr/include/c++/10.2.0/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<std::vector<int>&>’:
/usr/include/c++/10.2.0/bits/allocator.h:116:11: required from ‘class std::allocator<std::vector<int>&>’
/usr/include/c++/10.2.0/bits/stl_vector.h:87:21: required from ‘struct std::_Vector_base<std::vector<int>&, std::allocator<std::vector<int>&> >’
/usr/include/c++/10.2.0/bits/stl_vector.h:389:11: required from ‘class std::vector<std::vector<int>&, std::allocator<std::vector<int>&> >’
prova.cpp:5:5: required from ‘void f(Container&&) [with Container = std::vector<int>&]’
prova.cpp:10:8: required from here
/usr/include/c++/10.2.0/ext/new_allocator.h:62:26: error: forming pointer to reference type ‘std::vector<int>&’
62 | typedef _Tp* pointer;
| ^~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:63:26: error: forming pointer to reference type ‘std::vector<int>&’
63 | typedef const _Tp* const_pointer;
| ^~~~~~~~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:103:7: error: forming pointer to reference type ‘std::vector<int>&’
103 | allocate(size_type __n, const void* = static_cast<const void*>(0))
| ^~~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:120:7: error: forming pointer to reference type ‘std::vector<int>&’
120 | deallocate(_Tp* __p, size_type __t)
| ^~~~~~~~~~
...
When you have
template<typename Container>
void f(Container&) { // using && gives compilation error... (*)
std::vector<Container>{};
};
Constainer is deduced as a std::vector and all is well. When you use
template<typename Container>
void f(Container&&) { // using && gives compilation error... (*)
std::vector<Container>{};
};
and you don't use std::move, then Container is deduced as std::vector&, which is a reference type, and you cannot create vectors of a reference type, so you get an error.
When you use std::move(v), then you pass a std::vector&& to the function so Container again gets deduced to std::vector and the code compiles as it is not a reference type.

std::bind and rvalue reference

Let's consider the following piece of code:
class Widget{
};
int main(){
Widget w;
auto lambda = bind([](Widget&& ref){ return; }, std::move(w));
return 0;
}
and it triggers error
no match for call to ‘(std::_Bind<main()::<lambda(Widget&&)>(Widget)>) ()’
lambda();
And my question is: Why the error has appeared? After all, I do an explicit cast to rvalue reference – I mean std::move(w) and I take argument by rvalue reference – I mean Widget&& ref.
What's up?
Moreover the the below code works, what makes me worried the more:
class Widget{
};
int main(){
Widget w;
auto lambda = bind([](Widget& ref){ return; }, std::move(w));
return 0;
}
It might become clearer if you write down what std::bind schematically does.
// C++14, you'll have to write a lot of boilerplate code for C++11
template <typename FuncT, typename ArgT>
auto
bind(FuncT&& func, ArgT&& arg)
{
return
[
f = std::forward<FuncT>(func),
a = std::forward<ArgT>(arg)
]() mutable { return f(a); }; // NB: a is an lvalue here
}
Since you can call the function object std::bind gives you multiple times, it cannot “use up” the captured argument so it will be passed as an lvalue reference. The fact that you pass bind itself an rvalue only means that there is no copy made on the line where a is initialized.
If you try to compile your example with the schematic bind shown above, you'll also get a more helpful error message from your compiler.
main.cxx: In instantiation of ‘bind(FuncT&&, ArgT&&)::<lambda()> mutable [with FuncT = main()::<lambda(Widget&&)>; ArgT = Widget]’:
main.cxx:10:33: required from ‘struct bind(FuncT&&, ArgT&&) [with FuncT = main()::<lambda(Widget&&)>; ArgT = Widget]::<lambda()>’
main.cxx:11:31: required from ‘auto bind(FuncT&&, ArgT&&) [with FuncT = main()::<lambda(Widget&&)>; ArgT = Widget]’
main.cxx:18:59: required from here
main.cxx:11:26: error: no match for call to ‘(main()::<lambda(Widget&&)>) (Widget&)’
]() mutable { return f(a); }; // NB: a is an lvalue here
^
main.cxx:11:26: note: candidate: void (*)(Widget&&) <conversion>
main.cxx:11:26: note: conversion of argument 2 would be ill-formed:
main.cxx:11:26: error: cannot bind ‘Widget’ lvalue to ‘Widget&&’
main.cxx:18:33: note: candidate: main()::<lambda(Widget&&)> <near match>
auto lambda = bind([](Widget&&){ return; }, std::move(w));
^
main.cxx:18:33: note: conversion of argument 1 would be ill-formed:
main.cxx:11:26: error: cannot bind ‘Widget’ lvalue to ‘Widget&&’
]() mutable { return f(a); }; // NB: a is an lvalue here
To make it work you need to write it like this:
#include <functional>
#include <iostream>
class Widget{};
int main()
{
Widget a;
auto lf = [](Widget&& par){ };
auto f = std::bind
(
lf,
std::bind
(
std::move<Widget&>, a
)
);
f();
return 0;
}
My compiler is gcc version 4.9.2 20141101 (Red Hat 4.9.2-1) (GCC)

g++ 4.9.2 regression on pass reference to 'this'

This is a minimized part of Pointer to implementation code:
template<typename T>
class PImpl {
private:
T* m;
public:
template<typename A1>
PImpl(A1& a1) : m(new T(a1)) {
}
};
struct A{
struct AImpl;
PImpl<AImpl> me;
A();
};
struct A::AImpl{
const A* ppub;
AImpl(const A* ppub)
:ppub(ppub){}
};
A::A():me(this){}
A a;
int main (int, char**){
return 0;
}
It compilable on G++4.8 and prior and works as well. But G++4.9.2 compiler raise following errors:
prog.cpp: In constructor 'A::A()':
prog.cpp:24:15: error: no matching function for call to 'PImpl<A::AImpl>::PImpl(A*)'
A::A():me(this){}
^
prog.cpp:24:15: note: candidates are:
prog.cpp:9:5: note: PImpl<T>::PImpl(A1&) [with A1 = A*; T = A::AImpl]
> PImpl(A1& a1) : m(new T(a1)) {
^
prog.cpp:9:5: note: no known conversion for argument 1 from 'A*' to 'A*&'
prog.cpp:2:7: note: PImpl<A::AImpl>::PImpl(const PImpl<A::AImpl>&)
class PImpl {
^
prog.cpp:2:7: note: no known conversion for argument 1 from 'A*' to 'const PImpl<A::AImpl>&'
But it can be fixed by small hack. If i pass '&*this' instead of 'this' then it bring to compilable state.
Is it G++ regression or new C++ standards feature which eliminate backward compatibility?
We can make a simpler example that compiles on neither g++ 4.9 nor clang:
template <typename T>
void call(T& ) { }
struct A {
void foo() { call(this); }
};
int main()
{
A().foo();
}
That is because this is, from the standard, [class.this] (§9.3.2):
In the body of a non-static (9.3) member function, the keyword this is a prvalue expression whose value
is the address of the object for which the function is called.
You cannot take an lvalue reference to a prvalue, hence the error - which gcc explains better than clang in this case:
error: invalid initialization of non-const reference of type A*& from an rvalue of type A*
If we rewrite call to either take a const T& or a T&&, both compilers accept the code.
This didn't compile for me with gcc-4.6, so it seems that gcc-4.8 is where the regression occurred. It seems what you want is to take A1 by universal reference, ie: PImpl(A1 && a1). This compiles for me with both gcc-4.6, gcc-4.8 and gcc-4.9.

Error passing arguments to template function with const qualifier

I have this code example:
#include <iostream>
#include <memory>
template <typename T>
void func1(T& value)
{
std::cout << "passed 1 ..." << std::endl;
}
template <template <typename> class T, typename U>
void func2(T<U>& value)
{
std::cout << "passed 2 ..." << std::endl;
}
int main()
{
std::auto_ptr<int> a;
const std::auto_ptr<int> ca;
// case 1: using func1
func1(a); // OK
func1(ca); // OK
// case 2: using func2
func2(a); // OK
func2(ca); // Compilation error
return 0;
}
In the first case the function 'func1' accepts a generic argument regardless of the qualifier, however the second case the function 'func2' fails when the argument has the const qualifier. Why this happens ?
This is the compilation error:
make all
Building file: ../src/Test2.cpp
Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/Test2.d" -MT"src/Test2.d" -o "src/Test2.o" "../src/Test2.cpp"
../src/Test2.cpp: In function ‘int main()’:
../src/Test2.cpp:27: error: invalid initialization of reference of type ‘std::auto_ptr<int>&’ from expression of type ‘const std::auto_ptr<int>’
../src/Test2.cpp:11: error: in passing argument 1 of ‘void func2(T<U>&) [with T = std::auto_ptr, U = int]’
make: *** [src/Test2.o] Error 1
The problem is that in the case of func1, the compiler needs to deduce T and we get
T is std::auto_ptr<int> in the first call
T is const std::auto_ptr<int> in the second call
In both cases T is valid in itself.
Now for func2, the compiler needs to deduce T and U, where T is a template template parameter. What would be needed is:
T is std::auto_ptr, U is int in the first call
T is const std::auto_ptr, U is int in the second call
and there's your problem: T can not be const std::auto_ptr itself, as it combined a type-property const with a template std::auto_ptr which is not a valid type.