0

I asked a related, tedious question before and now I discover something new.

#include <iostream>
#include <string>
using namespace std;
void hello (const string &s1) {
    cout << "rocky" << endl;
}

void hello(string &s1) {
    cout << "banana" << endl;
}
int main()
{   
    string s = "abc";
    hello(const_cast<const string&>(s));  //how to explain this const_cast?
}

hello(const_cast<const string&>(s)); this works and match the const reference parameter function. So how's the conversion this time? Isn't it string to const string&?

Certainly I know that const reference can be initialized with non-const objects... But somehow I never take that as a conversion. I see it as an assignment. And I consider the type of reference and the type of referent as two very different stuff.

Rick
  • 7,007
  • 2
  • 49
  • 79
  • [`std::as_const`](http://en.cppreference.com/w/cpp/utility/as_const). – O'Neil Apr 12 '18 at 15:38
  • @PeteBecker so this `const_cast` can change something is a `string ` type to a `const string &` reference type? quite surprised if that's really the case. – Rick Apr 12 '18 at 15:39
  • Sorry, I deleted my original comment while I investigated something else. Yes, passing a `string` object by const reference is okay. Try removing the second version of `hello` and calling `hello(string)`; that's fine, and the call converts the `string` object to `const string&`. The reason for the cast in the current code is that `hello(string&)` is a better match for `hello(s)`; the cast forces the call to go to the first version of the function. – Pete Becker Apr 12 '18 at 15:42
  • Are you surprised that you can call `.size()` on a string, even though `.size()` is a `const` member function? Non-const objects can be converted to const references. – Simple Apr 12 '18 at 15:42

3 Answers3

1

So, primary meaning for the cast is to pick necessary one from the list of overloaded hello() functions. Without the cast, we pick non-const version, otherwise it's const one.

Secondly, why the cast of the string reference, not just string type? This is due limitations of the const_cast itself. Let's try to compile that:

hello(const_cast<const string>(s));  // we removed the "&"

compiler message:

error: invalid use of const_cast with type ‘const string {aka const std::__cxx11::basic_string<char>}’,
which is not a pointer, reference, nor a pointer-to-data-member type

So, const_cast is not intended to create new instance, instead it works with indirection to given instance and just changes associated CV qualification (as it's free lunch in terms of code generation). So, we have to deal with a pointer or reference to conform that condition.

P.S. As far as I know, C++17 allows creation of temporary copies for the casting (aka Temporary materialization) so our non-ref attempt could have practical meaning. Same time, it's quite new concept and not so wide-spread.

Yury Schkatula
  • 5,291
  • 2
  • 18
  • 42
1

Expressions, objects, variables - I know C++ can be a bit confusing to newcomers, especially when it comes to the subtle details.

So s here is a variable in main. While main runs (and that's the whole program, since it's main), there is an object that corresponds to the variable. And in main, the expression s refers to to that object. But there are many more expressions that refer to the same object. (s) is another expression that refers to the same object. So is *&s. Or *(&s).

s+"" is another expression that has the same value. But it's not the same object. Cf. integers i+0 or i*1.

References can be used to define variables which introduce new names for objects, names that can then be used in expressions. There are many places where it's convenient to be able to explicitly name something. It's just a new name, not a new object.

const_cast is another use for references. This however doesn't introduce a new name; it just forms a new expression with a const added to the type.

Now to your hello. This is an overloaded function. Overload resolution is done on the type of the expression used as an argument. So in hello(s1), s1 is the simple expression used for overload resolution, and its type is std::string. In hello(const_cast<std::string const&>(s1)), the expression is const_cast<std::string const&>(s1), and its type is obviously std::string const&.

Both expressions refer to the same object; they only differ in type. But that type difference is exactly what overload resolution needs.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

You can only convert from const reference to lvalue reference explicitly by using const_cast as such conversion is not safe and must be explicit. On another side you can convert from lvalue or lvalue reference to const reference implicitly or explicitly, as such conversion is safe. So usually nobody cast to const reference explicitly as it is unnecessary verbose and does not make sense. In your case it is converted explicitly to select certain overloaded function, because compiler would not convert it implicitly, as it can find overload with lvalue reference, but your example is rather artificial, as it is difficult to imagine why somebody would have such overload.

Slava
  • 43,454
  • 1
  • 47
  • 90