Reputation: 3557
Code sample that demonstrates the problem:
#include <functional>
#include <set>
void useCallback(std::function<void(char *)> callback)
{
}
int main()
{
std::function<void(char const *)> callback = [](char const *)
{
};
useCallback(callback);
return 0;
}
Yes, the const
removal is benign at the end of the day, useCallback()
states in its API that it is prepared to accept and use a callback that modifies its parameter, so it'll do just fine with a function that doesn't.
Why then does the argument that prevents passing a std::set<char *>
to a function that expects a std::set<char const *>
not apply here? That argument correctly points out that char *
and char const *
are different types, therefore the two sets are not type equivalent.
Keeping this in mind, why are the types of callback
and the parameter to useCallback()
not considered different?
-- Edit --
This is using MSVC, VS 2017.
Upvotes: 5
Views: 204
Reputation:
That argument correctly points out that
char *
andchar const *
are different types, therefore the two sets are not type equivalent.
std::function<void(char *)>
and std::function<void(char const *)>
are different types too, therefore the two function objects are not type equivalent. It's just that unlike with std::set
, they're not required to be type equivalent!
std::function<void(char *)>
has a constructor which will accept any object that can be called with a char *
argument. std::function<void(char const *)>
can be called with a char *
argument, so it is fine. It is this same constructor that's used when initialising a std::function<T>
from a lambda: there too the types are different, but as long as the lambda can be invoked when passed the argument types in T
, it is accepted.
Upvotes: 9
Reputation: 38267
The existing answers nicely explain the reasons why this is perfectly legal and what the difference to std::set
parameters is, I would like to contribute an example demonstrating that the type-safety isn't lost. Consider this
void useCallback(std::function<void(char *)> callback)
{
callback("abc");
}
This will compile, though it converts a string literal to const char*
, which gives you an appropriate warning with clang and gcc. This doesn't compile:
void useCallback(std::function<void(char *)> callback)
{
const char *str = "abc";
callback(str);
}
The original signature of std::function<void(char const *)>
is hence still there and well maintained by the compiler.
Upvotes: 1
Reputation: 37495
There is no problem here. const
is actually not being dropped here because calling std::function<void(char const *)>
function with arguments suitable for std::function<void(char *)>
(that is supplying char *
parameter) is perfectly fine. The erroneous case would be the opposite situation:
void useCallback(std::function<void(char const *)> callback)
{
}
int main()
{
std::function<void(char *)> callback = [](char *)
{
};
useCallback(callback);
return 0;
}
std::function<void(char *)>
can not be invoked with the arguments suitable for std::function<void(char const *)>
invocation. Therefore constructor of std::function<void(char const *)>
function object should yield an error.
Upvotes: 5