This is just a situation where C / C++ style const doesn't work very well. In reality, the kernel is not going to modify the arguments passed to exec(). It's just going to copy them when it creates a new process. But the type system is not expressive enough to really deal with this well.
A lot of people on this page are proposing making exec take "char**" or "const char * const[]". But neither of those actually works for your original example. "char**" means that everything is mutable (certainly not not true for the string constant "/usr/bin/whatever"). "const char *const[]" means that nothing is mutable. But then you cannot assign any values to the elements of the array, since the array itself is then const.
The best you could do is have a compile-time C constant like this:
const char * const args[] = {
"/usr/bin/whatever",
filename.c_str(),
someparameter.c_str(),
0};
This will actually work with the proposed type signature of "const char *const[]". But what if you need a variable number of arguments? Then you can't have a compile-time constant, but you need a mutable array. So you're back to fudging things. That is the real reason why the type signature of exec takes "const char **" for arguments.
The issues are the same in C++, by the way. You can't pass a std::vector < std::string > to a function that needs a std::vector < const std::string >. You have to typecast or copy the entire std::vector.