If we have this example functions code in C++
void foo(int x) { std::cout << "foo(int)" << std::endl; }
void foo(int& x) { std::cout << "foo(int &)" << std::endl; }
Is it possible to difference what function to call doing any modification in the calling arguments?
If the function foo is called in some of these ways:
foo( 10);
i = 10;
foo( static_cast<const int>(i));
foo( static_cast<const int&>(i));
it's called the first foo overloaded function, because it can't pass by reference a const argument to a non-const parameter.
But, how would you do to call the second foo overload function?
If I call the next way:
int i = 10;
foo( i);
It happens an ambiguous error because both functions are valid for this argument.
In this link https://stackoverflow.com/a/5465379/6717386 it's explained one way to resolve it: using objects instead of built-in types and doing private the copy constructor, so it can't do a copy of object value and it has to be called the second foo overload function and passing the object by reference. But, is there any way with the built-in types? I have to change the name of function to avoid the overloading?
You may do a cast (of the function) to select the overload function:
static_cast<void (&)(int&)>(foo)(i);
Demo
In most instance, function overloading involves distinct parameter types and different input parameter lengths.
Your attempt is generally a bad practice and the resulting compiled code is compiler dependent and code optimization may even worsen things even more.
You may consider simply adding a second parameter to the second method, something like this:
void foo(int x) { std::cout << "foo(int)" << std::endl; }
void foo(int& x, ...) { std::cout << "foo(int &, ...)" << std::endl; }
where ... could be a boolean type, say: bool anotherFunction
So calling foo(param1, param2) would simply call the second code and everybody is fine.
Very strange design, but if you want... I'll offer a solution as strange as your design Use Xreference in function signature. Then in the function you can check what you need to do using std::is_lvalue_reference, std::is_rvalue_reference.
Something like this
template<class T>
void foo(T&& x)
{
static_assert(std::is_same<std::decay_t<T>, int>::value, "!");
if (std::is_rvalue_reference<T&&>::value)
std::cout << "do here what you want in foo(int x)";
else
std::cout << "do here what you want in foo(int & x)";
}
int main()
{
int x = 5;
foo(x); //"do here what you want in foo(int x)" - will be printed
foo(std::move(x)); //"do here what you want in foo(int & x)" - will be printed
}
Despite the good answer of #Jarod42, as an alternative solution you can rely on a templated entry point and the overloading of an internal function (if you don't want to deal with explicit casts, of course).
It follows a minimal, working example:
#include<type_traits>
#include<iostream>
#include<utility>
void foo_i(char, int x) { std::cout << "foo(int)" << std::endl; }
void foo_i(int, int &x) { std::cout << "foo(int &)" << std::endl; }
template<typename T>
void foo(T &&t) {
static_assert(std::is_same<std::decay_t<T>, int>::value, "!");
foo_i(0, std::forward<T>(t));
}
int main() {
foo( 10);
int i = 10;
foo( static_cast<const int>(i));
foo( static_cast<const int &>(i));
foo(i);
}
The static_assert serves the purpose of checking the parameter to be something that involves int (that is int, int &, const int &, int &&`, and so on).
As you can see from the code above, foo(i) will print:
foo(int &)
As expected.
Another one:
#include <iostream>
#include <functional>
void foo(int x)
{
std::cout << "foo(int)\n";
}
template<typename T>
void foo(T&& x)
{
std::cout << "foo(int&)\n";
}
int main()
{
int i = 10;
foo(i); // foo(int)
foo(std::ref(i)); // foo(int&)
}
I just happened to have stumbled upon this post and was surprised not to find the typical SFINAE solution. So, there you go:
#include <iostream>
#include <type_traits>
template<typename T,
typename std::enable_if<!std::is_lvalue_reference<T>{}, int>::type = 0>
void foo(T)
{ std::cout << "foo(int)" << std::endl; }
template<typename T,
typename std::enable_if<std::is_lvalue_reference<T>{}, int>::type = 0>
void foo(T&)
{ std::cout << "foo(int &)" << std::endl; }
int main() {
int i = 42;
int& r = i;
foo<decltype(i)>(i);
foo<decltype(r)>(r);
}
Live example
Related
I am trying to create a map with string as key and a generic method as value in C++, but I do not know if that is even possible. I would like to do something like that:
void foo(int x, int y)
{
//do something
}
void bar(std::string x, int y, int z)
{
//do something
}
void main()
{
std::map<std::string, "Any Method"> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello", 1, 2);
}
Is that possible? If yes, how can I realise this?
You can type-erase the function types into a container, then provide a template operator(). This will throw std::bad_any_cast if you get it wrong.
N.B. because of the type erasure, you will have to specify exactly matching arguments at the call site, as e.g. std::function<void(std::string)> is distinct from std::function<void(const char *)>, even though both can be called with a value like "Hello".
#include <any>
#include <functional>
#include <map>
#include <string>
#include <iostream>
template<typename Ret>
struct AnyCallable
{
AnyCallable() {}
template<typename F>
AnyCallable(F&& fun) : AnyCallable(std::function(std::forward<F>(fun))) {}
template<typename ... Args>
AnyCallable(std::function<Ret(Args...)> fun) : m_any(fun) {}
template<typename ... Args>
Ret operator()(Args&& ... args)
{
return std::invoke(std::any_cast<std::function<Ret(Args...)>>(m_any), std::forward<Args>(args)...);
}
std::any m_any;
};
void foo(int x, int y)
{
std::cout << "foo" << x << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar" << x << y << z << std::endl;
}
using namespace std::literals;
int main()
{
std::map<std::string, AnyCallable<void>> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello, std::string literal"s, 1, 2);
try {
map["bar"]("Hello, const char *literal", 1, 2); // bad_any_cast
} catch (std::bad_any_cast&) {
std::cout << "mismatched argument types" << std::endl;
}
map["bar"].operator()<std::string, int, int>("Hello, const char *literal", 1, 2); // explicit template parameters
return 0;
}
The most (I cannot say best here) you can do is to use a signature erasure. That mean to convert the pointer to functions to a common signature type, and then convert them back to the correct signature before using them.
That can only be done in very special use cases (I cannot imagine a real world one) and will be highly unsecure: nothing prevent you to pass the wrong parameters to a function. In short: NEVER DO THIS IN REAL WORLD CODE.
That being said, here is a working example:
#include <iostream>
#include <string>
#include <map>
typedef void (*voidfunc)();
void foo(int x, int y)
{
std::cout << "foo " << x << " " << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar " << x << " " << y << " " << z << std::endl;
}
int main()
{
std::map<std::string, voidfunc> m;
m["foo"] = (voidfunc) &foo;
m["bar"] = (voidfunc)& bar;
((void(*)(int, int)) m["foo"])(1, 2);
((void(*)(std::string, int, int)) m["bar"])("baz", 1, 2);
return 0;
}
It gives as expected:
foo 1 2
bar baz 1 2
I could not find in standard whether this invokes or not Undefined Behaviour because little is said about function pointer conversions, but I am pretty sure that all common compilers accept that, because it only involve function pointers casting.
You cannot store functions with different signatures in a container like map, no matter if you store them as a function pointer or std ::function<WHATEVER>. The information about the signature of the function is one and only one in both cases.
The types for the value in map is one, meaning that the object stored in it are all of the same type.
So if your functions have all the same signature, then it's easy, otherwise, you have to abandon type safety and start walking in a very dangerous realm.
The one in which you erase the type information about the functions stored inside the map.
This translates to something like map<string, void*>.
I know that I shouldn't overload a function for just parameters differ only in one of them passed by copy and the other by reference:
void foo(int x)
{
cout << "in foo(int x) x: " << x << endl;
}
void foo(int& x)
{
cout << "in foo(int& x) x: " << x << endl;
}
int main()
{
int a = 1;
foo(5); // ok as long as there is one best match foo(int)
foo(a); // error: two best candidates so the call is ambiguous
//foo(std::move(a));
//foo(std::ref(an)); // why also this doesn't work?
}
So a code that uses std::bind can be like this:
std::ostream& printVec(std::ostream& out, const std::vector<int> v)
{
for (auto i : v)
out << i << ", ";
return out;
}
int main()
{
//auto func = std::bind(std::cout, std::placeholders::_1); // error: stream objects cannot be passed by value
auto func = std::bind(std::ref(std::cout), std::placeholders::_1); // ok.
}
So std::ref here to ensure passing by reference rather than by value to avoid ambiguity?
* The thing that matters me: Does std::bind() implemented some wrapper to overcome this issue?
Why I can't use std::ref in my example to help the compiler in function matching?
Now that you know passing by value and reference are ambiguous when overload resolution tries to compare them for choosing a best viable function, let's answer how would you use std::ref (or std::cref) to differentiate between pass-by-value and pass-by-reference.
It turns out to be ... pretty simple. Just write the overloads such that one accepts a int, and the other accepts a std::reference_wrapper<int>:
#include <functional>
#include <iostream>
void foo(int x) {
std::cout << "Passed by value.\n";
}
void foo(std::reference_wrapper<int> x) {
std::cout << "Passed by reference.\n";
int& ref_x = x;
ref_x = 42;
/* Do whatever you want with ref_x. */
}
int main() {
int x = 0;
foo(x);
foo(std::ref(x));
std::cout << x << "\n";
return 0;
}
Output:
Passed by value.
Passed by reference.
42
The function pass the argument by value by default. If you want to pass by reference, use std::ref explicitly.
Now let's answer your second question: how does std::bind deal with this type of scenario. Here is a simple demo I have created:
#include <functional>
#include <type_traits>
#include <iostream>
template <typename T>
struct Storage {
T data;
};
template <typename T>
struct unwrap_reference {
using type = T;
};
template <typename T>
struct unwrap_reference<std::reference_wrapper<T>> {
using type = std::add_lvalue_reference_t<T>;
};
template <typename T>
using transform_to_storage_type = Storage<typename unwrap_reference<std::decay_t<T>>::type>;
template <typename T>
auto make_storage(T&& obj) -> transform_to_storage_type<T> {
return transform_to_storage_type<T> { std::forward<T>(obj) };
}
int main() {
int a = 0, b = 0, c = 0;
auto storage_a = make_storage(a);
auto storage_b = make_storage(std::ref(b));
auto storage_c = make_storage(std::cref(c));
storage_a.data = 42;
storage_b.data = 42;
// storage_c.data = 42; // Compile error: Cannot modify const.
// 0 42 0
std::cout << a << " " << b << " " << c << "\n";
return 0;
}
It is not std::bind, but the method used is similar (it's also similar to std::make_tuple, which has the same semantic). make_storage by default copies the parameter, unless you explicitly use std::ref.
As you can see, std::ref is not magic. You need to do something extra for it to work, which in our case is to first decay the type (all references are removed in this process), and then check whether the final type is a reference_wrapper or not; if it is, unwrap it.
I am trying to create a map with string as key and a generic method as value in C++, but I do not know if that is even possible. I would like to do something like that:
void foo(int x, int y)
{
//do something
}
void bar(std::string x, int y, int z)
{
//do something
}
void main()
{
std::map<std::string, "Any Method"> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello", 1, 2);
}
Is that possible? If yes, how can I realise this?
You can type-erase the function types into a container, then provide a template operator(). This will throw std::bad_any_cast if you get it wrong.
N.B. because of the type erasure, you will have to specify exactly matching arguments at the call site, as e.g. std::function<void(std::string)> is distinct from std::function<void(const char *)>, even though both can be called with a value like "Hello".
#include <any>
#include <functional>
#include <map>
#include <string>
#include <iostream>
template<typename Ret>
struct AnyCallable
{
AnyCallable() {}
template<typename F>
AnyCallable(F&& fun) : AnyCallable(std::function(std::forward<F>(fun))) {}
template<typename ... Args>
AnyCallable(std::function<Ret(Args...)> fun) : m_any(fun) {}
template<typename ... Args>
Ret operator()(Args&& ... args)
{
return std::invoke(std::any_cast<std::function<Ret(Args...)>>(m_any), std::forward<Args>(args)...);
}
std::any m_any;
};
void foo(int x, int y)
{
std::cout << "foo" << x << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar" << x << y << z << std::endl;
}
using namespace std::literals;
int main()
{
std::map<std::string, AnyCallable<void>> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello, std::string literal"s, 1, 2);
try {
map["bar"]("Hello, const char *literal", 1, 2); // bad_any_cast
} catch (std::bad_any_cast&) {
std::cout << "mismatched argument types" << std::endl;
}
map["bar"].operator()<std::string, int, int>("Hello, const char *literal", 1, 2); // explicit template parameters
return 0;
}
The most (I cannot say best here) you can do is to use a signature erasure. That mean to convert the pointer to functions to a common signature type, and then convert them back to the correct signature before using them.
That can only be done in very special use cases (I cannot imagine a real world one) and will be highly unsecure: nothing prevent you to pass the wrong parameters to a function. In short: NEVER DO THIS IN REAL WORLD CODE.
That being said, here is a working example:
#include <iostream>
#include <string>
#include <map>
typedef void (*voidfunc)();
void foo(int x, int y)
{
std::cout << "foo " << x << " " << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar " << x << " " << y << " " << z << std::endl;
}
int main()
{
std::map<std::string, voidfunc> m;
m["foo"] = (voidfunc) &foo;
m["bar"] = (voidfunc)& bar;
((void(*)(int, int)) m["foo"])(1, 2);
((void(*)(std::string, int, int)) m["bar"])("baz", 1, 2);
return 0;
}
It gives as expected:
foo 1 2
bar baz 1 2
I could not find in standard whether this invokes or not Undefined Behaviour because little is said about function pointer conversions, but I am pretty sure that all common compilers accept that, because it only involve function pointers casting.
You cannot store functions with different signatures in a container like map, no matter if you store them as a function pointer or std ::function<WHATEVER>. The information about the signature of the function is one and only one in both cases.
The types for the value in map is one, meaning that the object stored in it are all of the same type.
So if your functions have all the same signature, then it's easy, otherwise, you have to abandon type safety and start walking in a very dangerous realm.
The one in which you erase the type information about the functions stored inside the map.
This translates to something like map<string, void*>.
This question already has answers here:
is any difference between std::forward<T> and std::forward<decltype(t)>?
(2 answers)
Closed 7 years ago.
I have something like (let's say):
template <typename Collection, typename Item>
void foo_collection(Collection&& c, Item && i) {
foo(std::forward<Collection>(c), std::forward<Item>(i));
}
I don't like the fact that std::forward<Collection>(c) is so long. I'd like to do this, instead:
template <typename Collection, typename Item>
void foo_collection(Collection&& c, Item&& i) {
foo(FWD(c), FWD(i));
}
I am thinking there must be a way to do this using decltype. I figure, given the decltype(i), if I remove all references and consts from it, I'll get Item, and then it should work:
#define FWD(v) \
std::forward< \
typename std::remove_const<\
typename std::remove_reference<\
decltype(v)>::type>::type>(v)
However, this doesn't work:
void foo(int& a) { cout << "ref" << endl; }
void foo(const int& a) { cout << "cref" << endl; }
template <typename T>
void call_foo(T&& t) { foo(FWD(t)); }
int main() {
int a = 10;
foo(10); // 1) prints cref
foo(a); // 2) prints ref
call_foo(10); // 3) prints cref
call_foo(a); // 4) prints cref
return 0;
}
Why does 4 call the cref overload, and not the ref overload?
I tried #define FWD(a) std::forward<decltype(a)>(a), and in this example it did work. However, I am guessing that it won't work in all cases. Is that the case, and if so, which cases won't it work in?
Finally, if the latter approach doesn't work, is there any way to write this macro in a way that works?
#define FWD(a) std::forward<decltype(a)>(a)
will work. So long as a is the name of a variable, decltype(a) is the type it was declared as, which is what you are supposed to pass to forward as its type parameter.
If you pass in an expression, things could get strange. I cannot think up a case where it fails, however.
Naturally this doesn't work for some more advanced uses of forward.
The approach
#define FWD(a) std::forward<decltype(a)>(a)
actually works as can seen here
void foo(int& a) { cout << "lref" << endl; }
void foo(const int& a) { cout << "cref" << endl; }
void foo(int&& a) { cout << "rref" << endl; }
template <typename T>
void call_foo(T&& t) { foo(FWD(t)); }
int main() {
int a = 10;
call_foo(10); // 3) prints rref
call_foo(a); // 4) prints lref
return 0;
}
A reference to array parameter is useful because it prevents decay, allowing you to deduce the size of an array.
template <typename T, std::size_t N>
constexpr std::size_t array_size(T (&) [N]) noexcept
{
return N;
}
But when is this useful?
template<typename T>
void foo(T& t)
{
// we now have a reference to bar
}
void bar() { }
int main()
{
foo(bar);
}
When do we care about preventing function to pointer decay? I'm asking about when this is useful, not why it isn't disallowed.
Like with objects, we us a pointer if “no object” (“no function”) is a sensible value and a reference if we wish to make sure that (unless somebody insists to shoot himself in the foot) there is always a valid object (function) referred to.
Consider the following poor man's function wrapper (wealthy man goes over there).
template<typename>
class Function; // never defined
template<typename ReturnT, typename... ArgT>
class Function<ReturnT(ArgT...)>
{
private:
ReturnT (*func_)(ArgT...);
public:
// Will also accept a 'nullptr'
Function(ReturnT (*func)(ArgT...)) noexcept : func_ {func}
{
}
ReturnT
operator()(ArgT... args)
{
return this->func_(args...);
}
};
Now we can write the following program, which works just fine.
#include <iostream>
int
add(int a, int b)
{
return a + b;
}
int
main()
{
Function<int(int, int)> f {add}; // ok
std::cout << f(5, 7) << std::endl; // ok, prints 12
}
However, we can also write the following program, which doesn't work so nice.
int
main()
{
Function<int(int, int)> f {nullptr}; // compiles fine
std::cout << f(5, 7) << std::endl; // compiles fine, crashes at run-time
}
Instead, if we had replaced the (*func) with (&func) in the template's definition,
// Won't accept a 'nullptr'
Function(ReturnT (&func)(ArgT...)) noexcept : func_ {func}
{
}
the line
Function<int(int, int)> f {nullptr}; // compile-time error
would have triggered a compile-time error.
To me the difference is like between normal variable reference and a pointer. References are nicer to deal with than pointers.
I had a use-case when I to customize a parts of an algorithm. It had some phases and I could switch between several options for each phase. This is of course achievable with pointers, but hey, you can use pointers instead of references pretty much everywhere too.
It looked similar to this:
#include <iostream>
using namespace std;
class Foo
{
public:
Foo(void(&t)()) : barish(t) { };
void fun() {barish();};
private:
void(&barish)();
};
void bar() { cout << "meow\n";}
void bark() { cout << "woof\n";}
int main()
{
Foo foo1(bar);
Foo foo2(bark);
foo1.fun();
foo2.fun();
}
You can customize fun and don't have to deal with dereferencing and asserts that it's not null.