dgnuff
dgnuff

Reputation: 3557

const change without const_cast<> Why no compiler warning/error?

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

Answers (3)

user743382
user743382

Reputation:

That argument correctly points out that char * and char 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

lubgr
lubgr

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

user7860670
user7860670

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

Related Questions