std functional wrapper of const member function - c++

#include <functional>
#include <iostream>
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int get_num(int i) { return num_;}
void set_num(int i) { num_ = i;}
int num_;
};
int main() {
std::function<int(const Foo *, int)> f_get_num;
f_get_num = &Foo::get_num;
return 0;
}
This will generate a error, error: invalid conversion from ‘const Foo*’ to ‘Foo*’ [-fpermissive] at line f_get_num = &Foo::get_num. Foo::get_num type is int (Foo:: *fp)(int). Can anybody explain it? Thanks.

You cannot call non-const functions on const objects. You can pass const Foo* to f_get_num, but Foo::get_num takes non-const implicit this.
The following two calls are just as illegal:
Foo foo;
Foo const* const_ptr = &foo;
const_ptr->get_num(42);
f_get_num(const_ptr, 42); // results in const_ptr->get_num(42)
You can declare your get_num to be const:
int get_num(int i) const { return num_;}
And then your code will work correctly.
The other way is to make your f_get_num take non-const parameter, but that's not the way to go when your function is a getter and shouldn't modify the object.
std::function<int(Foo*, int)> f_get_num;
f_get_num = &Foo::get_num;

Related

Deduced type with 'auto &&' as function return type

In the following snippet, the function B::f() is a "wrapper" around the function A::f(). But I assume that A::f() return type is an "opaque" type which I don't know if it has a value or reference semantics. So I cannot use auto or const auto & as return type for B::f(). I thought that auto && would do the trick, but it does not as auto && is deduced as A::OpaqueType &... Is it possible here to avoid writing A::OpaqueType?
#include <iostream>
struct A {
using OpaqueType=int; // But it could have been `const int &`
OpaqueType f() const {
return i;
}
int i=42;
};
struct B {
// how could I use `auto` here to avoid writing the `A::OpaqueType`?
// I would have expected that `auto &&` would do the trick but it does not
A::OpaqueType f() const {
return a.f();
}
A a;
};
int main()
{
B b;
std::cout << b.f() << std::endl;
}
The following snippet confuses me, as I would have expected that the return type of f() and g() would be int, but it is int & for f() and int && for g() (I don't even understand why it is not the same)... How this can be explained?
#include <iostream>
auto &&f() {
int i=42;
return i;
}
struct A {
int f() {return i;}
int i=42;
};
auto &&g() {
A a;
return a.f();
}
int main()
{
if (std::is_same_v<decltype(f()), int &>) {
std::cout << "f() return type is 'int &'\n";
}
if (std::is_same_v<decltype(g()), int &&>) {
std::cout << "g() return type is 'int &&'\n";
}
}
Thanks!
Pretty sure what you are looking for is decltype(auto). This will return by value if the expression in the return statement returns by value, and it will return by reference if the expression in the return statement returns by reference. That would give you
decltype(auto) f() const {
return a.f();
}

std::function incomplete type on const function

Consider the following code which works as expected:
#include <iostream>
#include <functional>
struct foo
{
std::function<int()> get;
};
struct bar
{
int get()
{
return 42;
}
};
int main()
{
foo f;
bar b;
f.get = std::bind(&bar::get, &b);
if (f.get())
std::cout << "f.get(): " << f.get() << std::endl;
return 0;
}
Now, let's assume that bar::get() is a const member function:
#include <iostream>
#include <functional>
struct foo
{
std::function<int()const> get;
};
struct bar
{
int get() const
{
return 42;
}
};
int main()
{
foo f;
bar b;
f.get = std::bind(&bar::get, &b);
if (f.get())
std::cout << "f.get(): " << f.get() << std::endl;
}
Using GCC 9.2, this snipped throws the following compiler error:
main.cpp:6:31: error: field 'get' has incomplete type 'std::function<int() const>'
6 | std::function<int()const> get;
| ^~~
In file included from /usr/local/include/c++/9.2.0/functional:59,
from main.cpp:2:
/usr/local/include/c++/9.2.0/bits/std_function.h:128:11: note: declaration of 'class std::function<int() const>'
128 | class function;
| ^~~~~~~~
I fail to understand why foo::get has incomplete type.
Could somebody point me towards the right direction for understanding this behavior and "fixing" it accordingly?
I have the need to bind a const member function to a function pointer.
int()const is an abominable type.
std::function<int()const> is not a type, because it doesn't match the only defined specialisation
namespace std {
template< class R, class... Args >
class function<R(Args...)> { ... };
}
Just use std::function<int()>.
The const bit only makes sense for member functions. You've already bound bar::get to an instance b to save it as a std::function.
As mentioned by #KamilCuk:
The constness is checked at std::bind, not at std::function
You don't need to pass explicit const to std::function. Just use your older prototype: std::function<int()>. It will work if you don't have const overload (that mean, you have either one of these int bar::get() or int bar::get() const) for the same member function (Otherwise, you need to type cast explicity).
Actually, your function (int bar::get() const) will be having a signature like this (behind the scenes):
// int bar::get() const
int get(const bar *const this)
{
return 42;
}
// int bar::get()
int get(bar *const this)
{
return 42;
}
If you have overloads and want to bind specific member function, you can do something like this:
typedef int(bar::*fptr)(void) const; // or remove const
std::bind((fptr)&bar::get, &b );
See this:
#include <iostream>
#include <functional>
#include <vector>
struct foo
{
std::function<int()> get;
};
struct bar
{
int get()
{
return 42;
}
int get() const
{
return 50;
}
};
int main()
{
foo f;
bar b;
typedef int (bar::*fptr)(void);
typedef int (bar::*fcptr)(void) const;
f.get = std::bind((fptr)&bar::get, &b);
if (f.get())
std::cout << "f.get(): " << f.get() << std::endl;
f.get = std::bind((fcptr)&bar::get, &b);
if (f.get())
std::cout << "f.get(): " << f.get() << std::endl;
}
Output:
f.get(): 42
f.get(): 50
std::bind does not pass through constness to the callable. Therefore, the following would work:
struct foo {
std::function<int()> get;
// ^ note there is no 'const'
};

Associative container to handle one element polymorphically

Suppose I have the following setup:
#include <iostream>
#include <map>
using namespace std;
class A
{
public:
A() { val1 = 1;}
~A() { }
private:
int val1;
};
class B
{
public:
B() { val2 = 1;}
~B() { }
int getVal() {return val2;}
private:
int val2;
};
class C : public A, public B
{
int val3;
};
void fun(std::pair<int, B>& p) {
cout << "B val: " << p.second.getVal() << endl;
}
void fun2(B& b) {
cout << "B val: " << b.getVal() << endl;
}
int main(int argc, const char *argv[])
{
map<int, C> m;
m.insert(make_pair(1, C()));
m.insert(make_pair(2, C()));
//fun(*(m.begin())); // <---- Compilation error
fun2(m.at(1)); // Works correctly
return 0;
}
Code compiles successfully and works as expected when I make the call to fun2. However if I uncomment the line fun(*(m.begin()) I get the following compilation error:
a.cpp: In function ‘int main(int, const char**)’:
a.cpp:48:18: error: invalid initialization of reference of type ‘std::pair<int, B>&’ from expression of type ‘std::pair<const int, C>’
fun(*(m.begin()));
^
a.cpp:33:6: error: in passing argument 1 of ‘void fun(std::pair<int, B>&)’
void fun(std::pair<int, B>& p) {
Is there any way to make the compiler handle the second element of std::pair polymorphically?
P.S. Sorry if the title is misleading in any way. Couldn't find a better way of expressing this.
This:
*(m.begin())
evaluates to a nameless temporary object. To bind this to a reference, the reference must be const, so:
void fun(const std::pair<int, B>& p) {
and for that function to call getVal(), that must be const too:
int getVal() const {return val2;}

invalid initialization of non-const reference of type ‘foo*&’ from an rvalue of type ‘foo*’?

Imagine this simple code, why does it get compile error?
#include <iostream>
using namespace std;
class foo{
public:
int *b;
foo(int a) {
b = NULL;
}
};
void bar(foo *&a) {
cout << "OK?" << endl;
}
int main() {
foo a(2);
bar(&a);
return 0;
}
I know I can use bar(foo *a) but why with & symbol it doesn't work and how can I make it work?
To make it work, change main to:
foo *p = &a;
bar(p);
The use of an lvalue reference (foo* &) means that the argument must be an lvalue. p is an lvalue because it is the name of a variable. &a is a prvalue.
If you want to change bar but not main then you could use any of foo *a, foo * const &a, or foo * && a, which can be initialized with an rvalue.

How does the compiler decide which function to call?

supposed there are two overloaded member function(a const version and a non-const version) in the String class:
char & String::operator[](int i) //Version 1
{
cout<<"char & String::operator[](int i) get invoked."<<std::endl;
return str[i];
}
const char & String::operator[](int i) const //Version 2
{
cout<<"const char & String::operator[](int i) const get invoked."<<std::endl;
return str[i];
}
and there is a test code fragment
int main(){
String a;
cout<<a[0]<<endl; //Line 1
a[0]='A'; //Line 2
}
How does the compiler decide which function to call? I found that Version 1 always got called when I run the program. Could anybody tell me why it is? And how can Version 2 got called?
If a is const, the second overload will get called.
int main(){
const String a;
cout<<a[0]<<endl; // would call const version
a[0]='A'; // will not compile anymore
}
If the object is const, the the const member function will be called. If the object is non-const the non-const member-function is called.
Exception
If their is only the const function, it is called in any case.
#include <iostream>
using namespace std;
class Foo {
public:
void print() {
cout << "Foo non-const member function\n";
}
void print() const {
cout << "Foo const member function\n";
}
};
class Bar {
public:
void print() const {
cout << "Bar const member function\n";
}
};
int main() {
Foo foo_non_const;
const Foo foo_const;
Bar bar_non_const;
const Bar bar_const;
foo_non_const.print();
foo_const.print();
bar_non_const.print();
bar_const.print();
return 0;
}
$ ./blah
Foo non-const member function
Foo const member function
Bar const member function
Bar const member function