1
votes

What does char [5] const & mean?

Constant lvalue reference to array of 5 char
Or lvalue reference to array of 5 constant char
Or lvalue reference to const array of 5 char?

I am reading C++ programming language book and I am learning about pointer to char. I found this code: char s[] = "Gorm";, which reminds me that string literal is converted implicitly to const char *.

So, I felt confused that LHS and RHS are not of same type.

I used online compiler and code from here to get idea about how compiler see type of LHS and RHS. Then, I found that LHS is seen as char[5] while RHS is seen as char[5] const &.

I can interpret LHS but, I cannot understand what is "constant lvalue reference to array of 5 char" or even after implicit conversion of LHS char s[] to char* s, what is "constant lvalue reference to non const pointer to non const char"?

Is not lvalue reference by definition constant "it refers only to value initializing it"? So, why do we need const before &?

And then, how can LHS and RHS of different types be assigned?

Following is the code, I used to get type of LHS and RHS:

#include < type_traits > 
#include < typeinfo > 
#ifndef _MSC_VER
    #include < cxxabi.h > 
#endif
#include < memory > 
#include < string > 
#include < cstdlib > 
#include < iostream > // std::cout

template < class T > std::string
type_name() {
    typedef typename std::remove_reference < T >:: typeTR;
    std::unique_ptr < char, void( *)(void *) > own(#ifndef _MSC_VER abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr), #else nullptr, #endif std::free);
    std::string r = own != nullptr
        ? own.get()
        : typeid(TR).name();
    if (std::is_const < TR >:: value) 
        r += " const";

    if (std::is_volatile < TR >:: value) 
        r += " volatile";

    if (std::is_lvalue_reference < T >:: value) 
        r += "&";
     else if (std::is_rvalue_reference < T >:: value) 
        r += "&&";

    return r;
}

int & foo_lref();
int && foo_rref();
int foo_value();
int main() {
    int i = 0;
    const int ci = 0;
    char s[] = "Gorm";
    std::cout << "decltype(s) is " << type_name < decltype("Gorm") > () << '\n';
    std::cout << "decltype(i) is " << type_name < decltype(i) > () << '\n';
    std::cout << "decltype((i)) is " << type_name < decltype((i)) > () << '\n';
    std::cout << "decltype(ci) is " << type_name < decltype(ci) > () << '\n';
    std::cout << "decltype((ci)) is " << type_name < decltype((ci)) > () << '\n';
    std::cout << "decltype(static_cast<int&>(i)) is " << type_name < decltype(static_cast < int &> (i)) > () << '\n';
    std::cout << "decltype(static_cast<int&&>(i)) is " << type_name < decltype(static_cast < int &&> (i)) > () << '\n';
    std::cout << "decltype(static_cast<int>(i)) is " << type_name < decltype(static_cast < int > (i)) > () << '\n';
    std::cout << "decltype(foo_lref()) is " << type_name < decltype(foo_lref()) > () << '\n';
    std::cout << "decltype(foo_rref()) is " << type_name < decltype(foo_rref()) > () << '\n';
    std::cout << "decltype(foo_value()) is " << type_name < decltype(foo_value()) > () << '\n';
}
3
Please don’t intentionally break the automated text formatting by putting spaces at the end of lines, it makes the text much harder to read. The automatic formatting is applied for a reason. - Konrad Rudolph
Your link is broken. Please post the entire code in the question itself. - Bartek Banachewicz
ok i just tried to make the text more clear :) - code_for_ever
not sure if I understnad the question (imho it is too many question into one). I think part of your confusing is from a misunderstanding of const. There is nothing wrong about const int x = 4; int y = x; - 463035818_is_not_a_number
@formerlyknownas_463035818 i am confused about usage of const before &>>>i interpret this as constant lvalue reference...by definition references must be constant and can not be changed after initialization - code_for_ever

3 Answers

3
votes

but i can not understand what is "constant lvalue reference to array of 5 char"

This is a misnomer; it's a reference to a constant array, not a constant reference to an array. As you've observed, references can't be changed, and const can't apply to them (because they're not objects).

and then how can lhs and rhs of different types be assigned?

You can create a reference to a const object from a value of non-const object. This makes sense, because you're turning a "view" that is read and write and constraining it into "read-only."

3
votes

char [5] const & is not a valid type. If the code you posted gives you this output, the code is broken.

Here's how you can check if a type is valid:

using type = char [5] const &; // error: expected ';' before 'const'

The actual type of string literals is const char [N], but in this case it doesn't matter.

C++ has a special rule that allows initializing character arrays with string literals. That's all.


Note that if you apply decltype to a string literal, it will give you const char (&)[N] (reference to a constant array of char). But it's not the actual type of the literal.

It might sound confusing, but expressions never have reference types. Variables do, but not expressions. See: What expressions yield a reference type when decltype is applied to them?

If decltype (when applied to an expression, rather than a variable) gives you an lvalue-reference type, it's an indication that the expression is an lvalue. (a rvalue-reference indicate an xvalue, and a lack of reference indicates a prvalue).

2
votes

To eliminate confusion with "weird" types, it's important to know how to read them. The clockwise/spiral method is one way to go here. Another effective method is the right/left sweep method. While these rules work best when there's a named type, it can still be adapted by simply reading right to left.

So, char[5] const & is a[n l-value] reference to a constant array of size 5 of characters. And that sounds like an accurate description of a string literal. In saying that the array is constant, it just means that the contents can't change, which is true of a string literal.

As another example, consider an array: int arr[5]. You are allowed to change the contents of the array, but arr must always point to the first element, meaning that pointer cannot change. Declaring a pointer that will always point to the same thing is accomplished with: int * const ptr;. I can change the value of what I'm pointing at, but I cannot change where I point at. If I declare it const int * const ptr or int const * const ptr, I can not change the value of what I'm pointing at, nor can I change where I point.