Here is an simplified version of the problem from ported from large code base. I've solved the issue, but I don't like the way I solved it.
The problematic code that doesn't compile is this I'm starting with:
#include <iostream>
#include <cstdlib>
#include <vector>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <array>
#include <utility>
#include <set>
#include <functional>
class S {
public:
int a;
int b;
mutable int c;
void set_c() { c = 222; }
};
struct cmp
{
bool operator()(const S& lhs, const S& rhs) const
{
return !(lhs.a == rhs.a && lhs.b == rhs.b);
}
};
class core {
public:
std::set<S, cmp> set_of_S;
std::function<void()> f;
void set_fn() {
f = [this]() {
auto it = set_of_S.begin();
it->set_c();
};
}
};
int main()
{
core core;
S a {.a = 2, .b = 3, .c = 0};
S b {.a = 2, .b = 3, .c = 0};
S c {.a = 2, .b = 4, .c = 0};
core.set_of_S.insert(a);
core.set_of_S.insert(b);
core.set_of_S.insert(c);
core.set_fn();
core.f();
std::cout << core.set_of_S.size() << '\n';
}
The compiler error is:
prog.cc: In lambda function:
prog.cc:37:23: error: passing 'const S' as 'this' argument discards qualifiers [-fpermissive]
it->set_c();
Ok, makes sense. As some people have told me, you should use the keyword mutable as this is not captured as a const and iterator it should be modifiable now (or atleast what I'm expecting):
void set_fn() {
f = [this]() mutable {
auto it = set_of_S.begin();
it->set_c();
};
}
This doesn't compile. This part doesn't make sense to me. So a member function cannot modify captured this inside lambda, but if you try to directly modify S::c inside the lambda compiler thinks that is okay. What? Doesn't make sense to me.
When I change:
void set_c() { c = 222; }
to
void set_c() const { c = 222; }
It will finally compile, but I don't like the solution, because we had to modify the original function signature just because the lambda won't accept it and it makes it less readable. I see lambdas as a tool and not something you have to design against. I have tried placing mutable keyword all over the place, but can't get it to compile. And I think there should be a way to permit member function to modify it's own state inside lambda.
Am I missing something or is this a compiler bug?
Here is the problematic code in wandbox: https://wandbox.org/permlink/qzFMW6WIRiKyY3Dj
I know this has been asked in: error: passing xxx as 'this' argument of xxx discards qualifiers but answers won't discuss on using mutable which to my understanding should solve these kind of situations.
Elements of a std::set<T> are unmodifiable - set_of_S.begin() returns a constant iterator: cppreference
Because both iterator and const_iterator are constant iterators (and may in fact be the same type), it is not possible to mutate the elements of the container through an iterator returned by any of these member functions [begin/cbegin].
That means that the element pointed to by the iterator it is const, so you can't call a non-const function such as set_c on it. it->c = 300 still works because you've made c mutable. It has nothing to do with the lambda you're calling this in being mutable or not.
Related
I have a tricky problem and I'm working on it for several hours but can't find a cause and solution of it. Hope someone help me.
I have to demonstrate function being called inside another function( pls see the comment in seminar.cpp)
Below are the files ( I have separated it into header and code files)
main.cpp
#include <iostream>
#include <functional>
#include "seminar.h"
int main()
{
Tom::Car::Car car;
Nor::Driving drivingnow;
std::vector<uint8_t> X = car.road(drivingnow);
for(int i = 0 ; i < X.size() ; i++){
std::cout<<unsigned(X[i])<<" ";
}
return 0;
}
seminar.h
#pragma once
#include "dist.h"
#include <vector>
#include <bits/stdc++.h>
namespace Tom
{
namespace Car
{
class Car
{
public:
std::vector<uint8_t> road(Nor::Driving &driving);
};
} // namespace Car
} // namespace Tom
seminar.cpp
#include "seminar.h"
#include <algorithm>
#include <functional>
namespace Tom
{
namespace Car
{
std::vector<uint8_t> drive(Nor::Range &range)
{
std::vector<uint8_t> s;
s.push_back(range.z);
s.push_back(range.zz);
return s;
}
template <typename T, typename B, typename L>
std::vector<uint8_t> Content(T Sec, B Byte, L Func)
{
Nor::Range Rom;
std::vector<uint8_t> z = Func(Rom);
return z;
}
std::vector<uint8_t> Car::road(Nor::Driving &driving)
{
std::function<std::vector<uint8_t>(Nor::Range &)> Func = &drive;
return Content(driving, 1, Func); // passing drive function into content
}
} // namespace Car
} // namespace Tom
dist.h
namespace Nor
{
class Driving{
public:
int x = 1;
};
class Range{
public:
int z = 50;
int zz = 100;
};
}
The above code and file structure works correctly and give me the correct expected output ie 50 100
Live here
Now I want to do more separation ie I want the implementation of drive function to move in another file ie in type.cpp
type.cpp
#include <algorithm>
#include "seminar.h"
#include <functional>
namespace Tom
{
namespace Car
{
std::vector<uint8_t> Car::drive(Nor::Range &range)
{
std::vector<uint8_t> s;
s.push_back(range.z);
return s;
}
} // namespace Car
} // namespace Tom
seminar.h
#pragma once
#include "dist.h"
#include <vector>
#include <bits/stdc++.h>
namespace Tom
{
namespace Car
{
class Car
{
public:
std::vector<uint8_t> road(Nor::Driving &driving);
std::vector<uint8_t> drive(Nor::Range &range);
};
} // namespace Car
} // namespace Tom
seminar.cpp
#include "seminar.h"
#include <algorithm>
#include <functional>
namespace Tom
{
namespace Car
{
template <typename T, typename B, typename L>
std::vector<uint8_t> Content(T Sec, B Byte, L Func)
{
Nor::Range Rom;
std::vector<uint8_t> z = Func(Rom);
return z;
}
std::vector<uint8_t> Car::road(Nor::Driving &driving)
{
std::function<std::vector<uint8_t>(Nor::Range &)> Func = &drive;
return Content(driving, 1, Func);
}
} // namespace Car
} // namespace Tom
Live here
After doing this I am getting an below error:
seminar.cpp: In member function ‘std::vector<unsigned char> Tom::Car::Car::road(Nor::Driving&)’:
seminar.cpp:22:71: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say ‘&Tom::Car::Car::drive’ [-fpermissive]
22 | std::function<std::vector<uint8_t>(Nor::Range &)> Func = &drive;
| ^~~~~
seminar.cpp:22:71: error: conversion from ‘std::vector (Tom::Car::Car::*)(Nor::Range&)’ to non-scalar type ‘std::function(Nor::Range&)>’ requested
Taking reference from this answer
I tried this way :
std::function<std::vector<uint8_t>(Nor::Range)> f = std::bind(&Car::drive, this);
And Got this error:
/usr/include/c++/9/functional:775:7: error: static assertion failed: Wrong number of arguments for pointer-to-member
774 | static_assert(_Varargs::value
| ~~~~~
775 | ? sizeof...(_BoundArgs) >= _Arity::value + 1
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
776 | : sizeof...(_BoundArgs) == _Arity::value + 1,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
seminar.cpp: In member function ‘std::vector<unsigned char> Tom::Car::Car::road(Nor::Driving&)’:
seminar.cpp:23:73: error: conversion from ‘std::_Bind_helper (Tom::Car::Car::*)(Nor::Range&), Tom::Car::Car*>::type’ {aka ‘std::_Bind (Tom::Car::Car::*(Tom::Car::Car*))(Nor::Range&)>’} to non-scalar type ‘std::function(Nor::Range)>’ requested
23 | std::function<std::vector<uint8_t>(Nor::Range)> f = std::bind(&Car::drive, this);
| ~~~~~~~~~^~~~~~~~~~~~~~~~~~~
seminar.cpp:25:40: error: ‘Func’ was not declared in this scope
25 | return Content(driving, 1, Func);
See here live
I don't know correctly what I am doing wrong in moving the implementation of drive function can someone please help with implementing the corrrect way.
Note:: I'm fine if the solution uses another way to pass the function ie by not using std::function . Thanks
In seminar.cpp, here:
std::function<std::vector<uint8_t>(Nor::Range &)> Func = &drive;
drive is a member function. It needs the this pointer to be called.
You can easily solve this by wrapping it in a lambda:
std::function<std::vector<uint8_t>(Nor::Range &)> Func = [this](Nor::Range & r) {
return this->drive(r);
};
If you prefer the std::bind method that you tried, you need a placeholder for the Nor::Range& parameter:
std::function<std::vector<uint8_t>(Nor::Range &)> Func = std::bind(&Car::drive, this, std::placeholders::_1);
Also, you really don't need the std::function at all since it's a local variable that you immediately pass to another function, so just use auto instead (or pass it directly without an intermediate variable):
auto Func = [this](Nor::Range & r) {
return this->drive(r);
};
Non-static member function pointers should be initialized in this way:
return_type (class_name::*pointer_name)(argument_types ...) = &class_name::function_name;
They should be called in this way:
(instance_name.*pointer_name)(arguments ...);
Such pointers can be assigned to std::function objects, which is a bit complicated. You should pay attention that this is an implicit argument in a member function, so the pointer type of the class should be explicitly declared as an argument in the template of the std::function object in such an assignment. For example,
std::function<std::vector<uint8_t>(Car *, Nor::Range &)> Func = &Car::drive;
You also mentioned the std::bind function. In this case, you should use std::placeholders to hold the place for undetermined arguments. Pay attention that the template of the std::function object should only contain undetermined arguments.
std::function<std::vector<uint8_t>(Nor::Range &)> Func = std::bind(&Car::drive, this, std::placeholders::_1);
You may try auto:
auto Func = std::bind(&Car::drive, this, std::placeholders::_1);
The std::placeholders::_1 allows the argument Nor::Range &range to be passed to the function Car::drive later. The implicit argument this should also be explicitly used in the std::bind function.
I am able to do this:
std::vector<int> vec = { 1, 2, 3, 4, 5 };
But I am not able to do this:
std::vector<const type_info&> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says pointer to reference is illegal
or
std::vector<const type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says Error C2338 The C++ Standard forbids containers of const elements because allocator is ill-formed.
or
std::vector<type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
Compiler says:
Error C2280 'type_info::type_info(const type_info &)': attempting to reference a deleted function
I am able not able to do push_back either.
What is the solution to have a vector or list of type_info?
You can use pointers
std::vector<const std::type_info*> v = { &typeid(Class1), &typeid(Class2) };
This is valid because typeid returns a reference to an object with static storage duration.
You cannot have a vector of references, for several fundamental reasons. C++ simply does not work this way. You can, however, employ std::reference_wrapper to get pretty much the same result:
#include <functional>
#include <vector>
#include <typeinfo>
class A {
};
int main()
{
std::vector<std::reference_wrapper<const std::type_info>> avec;
auto &t=typeid(A);
avec.push_back(t);
const std::type_info &i=avec[0];
return 0;
}
You can't have arrays of references so you could wrap them in std::reference_wrappers:
#include <functional>
#include <typeinfo>
#include <vector>
std::vector<std::reference_wrapper<const std::type_info>> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
The name ClassBlackList implies that you will search it a lot and also that the elements in the list are to be unique. In that case, you may want to use a std::set instead.
Example:
#include <functional>
#include <iostream>
#include <typeinfo>
#include <set>
struct Class1 {};
struct Class2 {};
struct Class3 {};
struct comp { // a functor to compare reference wrapped type_info's
std::size_t operator()(const std::reference_wrapper<const std::type_info>& lhs,
const std::reference_wrapper<const std::type_info>& rhs) const
{
return std::less<const std::type_info*>{}(&lhs.get(), &rhs.get());
}
};
int main() {
std::set<std::reference_wrapper<const std::type_info>, comp> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
// try to insert typeid(Class3) twice, it only succeeds the first time
auto[it1, inserted1] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted1 << '\n';
auto[it2, inserted2] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted2 << '\n';
}
Output:
inserted: 1
inserted: 0
#include <functional>
#include <memory>
#include <string>
#include <iostream>
void foo(std::function<void()> f)
{
f();
}
int main()
{
std::unique_ptr<int> handle = std::make_unique<int>(5);
foo(
[h = std::move(handle)]() mutable
{
std::cout << *h << std::endl;
});
}
Following code doen't compile with error
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29910\include\functional(914,58): error C2280: 'main::<lambda_1aa523ba59bbecea5306bc1fd178120f>::<lambda_1aa523ba59bbecea5306bc1fd178120f>(const main::<lambda_1aa523ba59bbecea5306bc1fd178120f> &)': attempting to reference a deleted function
Why it is happen?
Is there are way how pass std::unique_ptr in std::function<void()> without passing it by reference?
The std::function requires the function object to be Copy-Constructible, so you can't expect a lamdba to be moved to it. On initialization, it attempts to copy the lambda and so the std::unique_ptr with it, which is a member of this lambda, and, expectedly, fails to do so. What you can do is store your lambda in a variable and pass it to function that accepts const std::function& using std::ref like that:
void foo(const std::function<void()>& f); // function declaration
auto a = [h = std::move(handle)]() mutable
{
std::cout << *h << std::endl;
};
foo(std::ref(a));
This is a related question with much more detailed answers: How to create an std::function from a move-capturing lambda expression?
Stacked people.
Iam trying to implement an observer(esque?) pattern for my program. I have a component which stores what functions should be called if an event occours. My prolem is that i dont know how should i erase my function from the container, if the need arises. Tried storing the functions by reference, but iam not sure how to do that(or if thats possible.)
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
enum class EVENT_TYPE{
anEvent
};
class EventableComponent{
map<EVENT_TYPE, vector<function<void()>>> listeners;
public:
void trigger(EVENT_TYPE _et){
for(auto& it : listeners[_et]){
it();
}
}
void registerListener(EVENT_TYPE _et, function<void()> _fn){
listeners[_et].push_back(_fn);
};
void removeListener(EVENT_TYPE _et, function<void()> _fn){
//error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::function<void (void)>'
//(or there is no acceptable conversion)
listeners[_et].erase(remove(listeners[_et].begin(), listeners[_et].end(), _fn), listeners[_et].end());
};
};
int main(){
EventableComponent ec;
// this would become a member function for a class somewhere down the line
auto fn = [](){cout << "Hello.\n"; };
ec.registerListener(EVENT_TYPE::anEvent, fn);
ec.trigger(EVENT_TYPE::anEvent);
ec.removeListener(EVENT_TYPE::anEvent, fn);
ec.trigger(EVENT_TYPE::anEvent);
cin.get();
return 0;
};
Your problem can be reduced to the fact that two std::function instances cannot be compared for equality. std::remove requires operator==, and std::function does not have it. See "Why is std::function not equality comparable?".
Consider the following situation.
Let's say you defined two lambdas in your main:
auto fn = [](){cout << "Hello.\n"; };
auto fn2 = [](){cout << "Hello.\n"; };
Now, are those two equal or not? They do the same thing, but perhaps this is sheer coincidence. Would they become unequal if the second "Hello" became "Hello2"? Would they become unequal if the second one was no longer a lambda but a real function void f()?
The thing is that there can be no generally useful definition of equality for function objects, so it's up to you to define what equality really means in the context of your program.
You have several options to solve the problem at hand. One would be to operate on pointers to std::function objects. Pointers can be compared, and proper use of std::unique_ptr makes sure that deallocation is handled correctly.
Or you assign an identifier to every std::function you use. See the following modified example of your code in which direct storage of std::function<void()> in the vector is replaced with a custom type EventFunction that maps an int to the function object. The example uses std::remove_if to compare only the ints:
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
enum class EVENT_TYPE{
anEvent
};
struct EventFunction {
function<void()> f;
int id;
};
class EventableComponent{
map<EVENT_TYPE, vector<EventFunction>> listeners;
public:
void trigger(EVENT_TYPE _et){
for(auto& it : listeners[_et]){
it.f();
}
}
void registerListener(EVENT_TYPE _et, EventFunction _fn){
listeners[_et].push_back(_fn);
};
void removeListener(EVENT_TYPE _et, int function_id){
//error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::function<void (void)>'
//(or there is no acceptable conversion)
listeners[_et].erase(remove_if(listeners[_et].begin(), listeners[_et].end(),
[&](EventFunction const& e) { return e.id == function_id; }), listeners[_et].end());
};
};
int main(){
EventableComponent ec;
// this would become a member function for a class somewhere down the line
auto fn = [](){cout << "Hello.\n"; };
ec.registerListener(EVENT_TYPE::anEvent, EventFunction{ fn, 1 });
ec.trigger(EVENT_TYPE::anEvent);
ec.removeListener(EVENT_TYPE::anEvent, 1);
ec.trigger(EVENT_TYPE::anEvent);
};
Tried storing the functions by reference, but iam not sure how to do
that(or if thats possible.)
It's not possible because you cannot store references in standard-library containers. But I suppose the idea is similar to the one with pointers I mentioned above.
The following example from boost bind does not work for me:
#include <boost/bind.hpp>
struct A
{
int data;
};
int main()
{
A a;
boost::bind(&A::data, _1)(a) = 1;
}
error: assignment of read-only location 'boost::bind [with A1 = boost::arg<1>, M = int, T = A](&A::data, (<unnamed>::_1, boost::arg<1>())).boost::_bi::bind_t<R, F, L>::operator() [with A1 = A, R = const int&, F = boost::_mfi::dm<int, A>, L = boost::_bi::list1<boost::arg<1> >](((A&)(& a)))'
Am I doing anything wrong? The compiler is g++ 4.4.0
The result type of that bind expression is int (or rather const int&). I think you can override the return type:
boost::bind<int&>(&A::data, _1)(a) = 1;
UncleBens' solution is fine but I thought I'd add that if you use Boost.Lambda the problem disappears:
#include <boost/lambda/bind.hpp>
struct A {
int data;
};
int main() {
namespace bll = boost::lambda;
A a;
bll::bind(&A::data, bll::_1)(a) = 1;
}
And so it does if you use boost::mem_fn:
#include <boost/mem_fn.hpp>
struct A {
int data;
};
int main() {
boost::mem_fn(&A::data)(a) = 1;
}
I'm not sure what you want to do, but does Boost.Bind really overload the assignment operator? If you'd like to assign the value 1 to a.data using the returned function object I think you need to do something like this (also note that "a" needs to be bound by reference):
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <cassert>
void foo()
{
A a;
boost::bind(&A::data, _1)(boost::ref(a), 1);
assert(a.data == 1);
}
If you need to use the assignment operator I think that using Boost.Lambda or Boost.Phoenix would be a better choice.