syntax question:what does `char [5] const &` mean? - c++

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';
}

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).

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."

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.

Related

C++ - Why i can't use int*p=&arr (arr refers to an array) to init a pointer in C++? [duplicate]

int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;
output is
0x7fffd4d2f860
0x7fffd4d2f860
0x7fffd4d2f860
Now when i do this->
int *r=q; // allowed
int *r=&q[0] // allowed
int *r=&q // not allowed
Why is the third assignment not allowed when it is essentially the same thing?
If you have an array declared like
T a[N];
where T is some type specifier then a pointer to the array will be declared like
T ( *p )[N] = &a;
A general rule is the following. If you have a multidimensional array (including one-dimensional arrays) like for example
T a[N1][N2][N3];
then this declaration you may rewrite like
T ( a[N1] )[N2][N3];
To get a pointer to the first element of the array just substitute the content in the parentheses the following way
T ( *p )[N2][N3] = a;
If you want to get a pointer to the whole array then rewrite the declaration of the array like
T ( a )[N1][N2][N3];
and make the substitution
T ( *p )[N1][N2][N3] = &a;
Compare this with a declaration of a scalar object and a pointer to it.
For example
T obj;
You may rewrite the declaration like
T ( obj );
Now to get a pointer to the object you can write
T ( *p ) = &obj;
Of course in this case the parentheses are redundant and the above declaration is equivalent to
T *p = &obj;
As for this code snippet
int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;
and its output
0x7fffd4d2f860
0x7fffd4d2f860
0x7fffd4d2f860
then array designators used in expressions with rare exceptions are converted to pointers to their first elements.
So in fact the two expression q and &q[0] in these statements
cout << q << endl;
cout << &q[0] << endl;
are equivalent. On the other hand, the address of the array itself is the address of the memory extent that the array occupies. And in the beginning of the extent there is the first element of the array. So the three expressions give the same result: the address of the extent of memory occupied by the array.
Why is the third assignment not allowed when it is essentially the same thing?
Because The C++ language has a feature called "type safety". There is a type system that helps you keep the logic of your program sound.
One particular rule is that arbitrary pointer types can not be used to initialise pointers of other, incompatible types. In this case, you have a pointer to type int (.i.e. int*) that you try to initalise with an expression of type pointer to type array of 10 int (i.e. int(*)[10]). One type is not implicitly convertible to the other, hence the program is ill-formed.
Then why does cout print same things in all of the three cases?
Because all of the pointers have the same value. The first byte of the first element of the array is the same byte as the first byte of the entire array.
It just so happens that the stream insertion operator handles all pointer types1 exactly the same, so pointers with same value but different type produce the same output.
1 Pointers to character types are an exception. They are treated entirely differently.
Why can't we assign address of array to pointer?
Actually, we can assign address of an array to a pointer. We just cannot assign address of a array (or any other object for that matter) to a pointer of wrong type. We need a pointer to an array in this case:
int (*r)[10] = &q;
You cannot do the third assignment because &q's type is an int (*)[10], which is incompatible with the type of int* r.
The output of cout << &q does not reveal the type of &q. See this documentation link.
q is a fixed-length array. Specifying q by itself in an expression decays into a pointer to the 1st element of q. Thus, q decays to the same pointer value that &q[0] returns. &q, on the other hand, returns the memory address of the q variable itself, and for an array, its 1st element occupies that same memory address.
There is an operator<<(void*) defined for std::ostream, and void* can accept (almost) ANY type of pointer. Since all three of your cout calls resolve to the same memory address, and there is an operator<< that accepts all three types of pointers, that is why all three calls print the same number.
As for your assignments:
q is an int[10], which decays into an int*, which is why int *r=q; works.
&q[0] dereferences q to access its 1st element, which is an int, and then takes the address of that element, producing an int*, which is why int *r=&q[0]; works.
since q is an int[10], &q is an int(*)[10], which DOES NOT decay into an int*, which is why int *r=&q; does not work. You would have to declare r using the correct type:
int (*r)[10] = &q;
The following code demonstrates the differences between arr, &arr and &arr[0] where arr is an array of integers.
#include <iostream>
#include <string_view>
// Reference: https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/56766138#56766138
// Type finding code start
template <typename T>
constexpr auto type_name()
{
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
// Type finding code end
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
std::cout << "Value: " << arr << "\tType: " << type_name<decltype(arr)>() << std::endl;
std::cout << "Value: " << &arr << "\tType: " << type_name<decltype(&arr)>() << std::endl;
std::cout << "Value: " << &arr[0] << "\tType: " << type_name<decltype(&arr[0])>() << std::endl;
return 0;
}
See it in action.
Sample Output:
Value: 0x7ffcdadd22d0 Type: int [5]
Value: 0x7ffcdadd22d0 Type: int (*)[5]
Value: 0x7ffcdadd22d0 Type: int*
While all three are associated with the same memory location, they are of different types.
Reference: Is it possible to print a variable's type in standard C++?

Const references with and without type conversion - why feature type conversion at all? - C++

I began learning C++ this week, and currently I am reading about compound types and constant variables. Unlike in most cases, references to const support type conversion by creating a temporary variable. But if so, then what's the difference in behaviour between:
int i = 42;
double di = 42;
and
int i = 42;
const double &di = 42;
Don't we end up with two independent variables that can end up having different values if we try to change i? Is the only difference that in the example with the const reference, the reference cannot be changed? The thing that bugs me the most is that when the types of a non-const variable and a const ref match, the reference points to the same address in memory and changes along with the change in the original variable, whereas this does not happen for a non-typematching const ref to a non-const variable:
#include <iostream>
int main() {
int i = 42;
const int &ri = i;
const double &dri = i;
++i;
std::cout << " at " << &i << ", " << ri << " at "
<< &ri << ", " << dri << " at " << &dri << std::endl;
int j = i;
int jj = ri;
int djj = dri;
std::cout << j << " at " << &j << ", " << jj << " at "
<< &jj << ", " << djj << " at " << &dri << std::endl;
return 0;
}
Output:
43 at %Address1%, 43 at %Address1%, 42 at %Address2%
43 at %Address3%, 43 at %Address4%, 42 at %Address2%
This seems to me like a major difference in behavior that is easy to overlook from simply looking at the syntax, on top of the fact that such behavior seems counter-intuitive to the entire idea of references. Also, why does jj is allocated a separate space, but not djj, which references the same address as dri?
Let's say you have a function of the form:
void foo(double const& d);
And now, let's say you have a float somewhere. And you want to pass that to this function via foo(f);. If a T const& could not bind to any object convertible to T, then this wouldn't work. Every user of this function that don't have a double would have to do foo(static_cast<double>(f)) or an equivalent.
You might say that maybe foo should take double by value. And for double specifically, maybe it should.
But what about if it's std::string, and I want to call foo("some string"). Well, "some string" is not a std::string; it is a string literal which is convertible to std::string. So we allow that conversion.
Again, you might say that it should take the string by value. But what about the cases when the caller really does have a std::string? They'd have to copy that string, a copy that is discarded and is therefore unnecessary.
Of course, C++'s rules should be uniform. So if we want this to work for function arguments&parameters, it also has to work for named variables. But even then, it could be useful. You might call a function that you expect to return a string of some form, but aren't especially picky about which form. Just so long as it is convertible to a std::string. This might be in template code:
template<typename T>
void foo(T t)
{
std::string const& data = t.get_a_string();
}
Do you really care if get_a_string returns std::string exactly, or just some string type convertible to std::string? Probably the latter.

Am I invoking undefined behavior when casting back to my pointer?

I've been trying to figure out if this is an optimisation bug as it only seems to affect stack variables, and I wonder if there's some incorrect assumptions being made. I have this type which converts to and from a relative offset, and it has been working fine while using reinterpret_cast, but now I'm moving to static_cast, it's starting to cause issues in optimised builds. I need to get away from reinterpret_cast for safety certification reasons, so I don't have the option of keeping it as it is.
#include <iostream>
template <typename T>
class Ptr
{
public:
Ptr(const T* ptr = nullptr) : m_offset(GetOffset(ptr)) {}
T& operator*() const noexcept { return *GetPtr(); }
T* get() const noexcept { return GetPtr(); }
bool operator==(const T *ptr) const {
// comment this back in and it stops failing
//std::cout << "{op==" << get() << " == " << ptr << "}";
return get() == ptr;
}
private:
std::ptrdiff_t m_offset = 0;
inline T* GetPtr() const
{
auto offset = m_offset;
auto const_void_address = static_cast<const void*>(&m_offset);
auto const_char_address = static_cast<const char*>(const_void_address);
auto offset_address = const_cast<char*>(const_char_address);
auto final_address = static_cast<void*>(offset_address - offset);
return static_cast<T*>(final_address);
}
std::ptrdiff_t GetOffset(const void* ptr) const
{
auto void_address = static_cast<const void*>(&m_offset);
auto offset_address = static_cast<const char*>(void_address);
auto ptr_address = static_cast<const char*>(ptr);
return offset_address - ptr_address;
}
};
std::ostream& operator<<(std::ostream &stream, const Ptr<int> &rp) {
stream << rp.get();
return stream;
}
int main() {
int data = 123;
Ptr<int> rp(&data);
std::cout << "data " << data << " # " << &data << std::endl;
std::cout << "rp " << *rp << " get " << rp.get() << std::endl;
std::cout << (rp == &data) << std::endl;
std::cout << "(rp.get() == &data) = " << (rp.get() == &data) << std::endl;
std::cout << "(rp == &data) = " << (rp == &data) << std::endl;
return 0;
}
with optimisations turned on, I get output like this:
data 123 # 0x7ffe79544a34
rp 123 get 0x7ffe79544a34
0
(rp.get() == &data) = 0
(rp == &data) = 0
Which includes some output which is clearly inconsistent with itself.
I've tested this on GCC 8,9, and 11.2.
As soon as I drop back to -O0, it's fine.
If I uncomment the std::cout inside the operator==, it's fine.
If I go back to reinterpret_cast (return reinterpret_cast<T*>(reinterpret_cast<std::ptrdiff_t>(&m_offset) - offset);) it's fine.
If I allocate data as a pointer and initialise rp from that, it's fine too.
It seems to behave as I expect under clang (8 through 13 seem fine)
This feels like I am either not understanding where the UB is coming from, or there's a compiler optimisation bug here.
EDIT / UPDATE:
After looking at this is more detail I think the only solution is to do type punning in a semi-safe way, so I have tried this solution and it appears to work. (it appears, what I am now doing is part of C++20 and called bit_cast, so maybe this is valid?)
inline T* GetPtr() const
{
auto offset = m_offset;
intptr_t realAddress;
auto address_of_m_offset = &m_offset;
std::memcpy(&realAddress, &address_of_m_offset, sizeof( realAddress));
realAddress -= m_offset;
T *outValue;
std::memcpy(&outValue, &realAddress, sizeof( outValue));
return outValue;
}
std::ptrdiff_t GetOffset(const void* ptr) const
{
auto address_of_m_offset = &m_offset;
intptr_t myAddress;
std::memcpy(&myAddress, &address_of_m_offset, sizeof(myAddress));
intptr_t realAddress;
std::memcpy(&realAddress, &ptr, sizeof(realAddress));
return static_cast<ptrdiff_t>(myAddress - realAddress);
}
This no longer seems to cause a problem with GCC. I hear that std::memcpy is meant to be used for objects of same type where otherwise we would have used reinterpret_cast, so this makes sense to me.
Your undefined behaviour is in GetOffset.
This is how pointer subtraction is defined by the standard:
When two pointer expressions P and Q are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as std::ptrdiff in the <cstddef> header ([support.types.layout])
If P and Q both evaluate to null pointer values, the result is 0.
Otherwise, if P and Q both point to, respectively, array elements i and j of the same array object x, the expression P - Q has the value i - j.
Otherwise, the behavior is undefined.
And here, P (which has the address of m_object) and Q (which has the address of data) aren't elements of the same array, so this is undefined behaviour.
Addition and subtraction of pointers and integers is also defined in terms of array elements:
When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.
If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.
Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]), the expression P + J and J + P (Where J has value j point to the (possibly-hypothetical) array element i+j of x if 0≤i+j≤n and the expression P - J points to the (possibly-hypothetical) array element i-j of x if 0≤i-j≤n.
Otherwise, the behavior is undefined.
The pointer subtraction happens at offset_address - offset, where P is the address of m_offset and offset is probably some positive number. m_offset is the first element of the array, so i-j < 0
So, the compiler can see that GetPtr returns a pointer relative to the m_offset (in the char[sizeof(Ptr<int>)] array that aliases the object), so it cannot equal the address of data (without UB), and thus an optimizer can replace (rp.get() == &data) with false.
When you use ptrdiff_t, no such restriction on addition or subtraction exists. And though the standard doesn't guarantee that reinterpret_cast<char*>(reinterpret_cast<intptr_t>(char_pointer) + n) == char_pointer + n (pointers mapping linearly as you would expect) this is what happens when compiling with gcc on common architectures.

C++ Why can const X& be modified by function?

From another post, I test the example regarding const X& x in function. This link What does “const X& x” mean? says "It means x aliases an X object, but you can’t change that X object via x." But below example shows we can modify X object? Why?
#include <iostream>
using namespace std;
int a = 1;
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
cout << "n: " << n << endl;
//f(); // Might change the value of whatever n refers to
}
int main()
{
DoWork(a);
cout << "a: " << a << endl;
}
Follow up question:
For example, we have a function return const reference, why can it be assigned to non-const reference?
const X& tmp = get();
X& other = tmp; //this works, but why? this get rids of const X& in tmp?
I think you have confusion about const and references.
Firstly what is reference `
it's another name(alias) for an object.
About what's written in function parameters means that n is a const reference. It means you can't change a through n.(or in the simple sense, n can't be on the left side of the assignment operator. It also means that n is bounded to the object permanently.
n = n+2; // This is an error as n is const and hence you can't use n to modify the object it is pointing.
a = n+2; // here you are not using `n` to modify. So this is ok.
int x = 10 ;
n = x; // error as n is bounded to `a` so it can't be bounded again to any other variable x. You can see `n` is on left which is an error.
Now if you do this
cout<<a<<"\t"<<n<<endl; // Output 3; (1+2)
About follow up question. (Read it carefully)
Let us suppose a general scenario.
const int ci = 1024;
int &r2 = ci; // error: non const reference to a const object
The same is the case with your function question. You must be wondering why we can't do such a thing.
See here c1 is const so we can't change it. Now suppose(JUST SUPPOSE) the following code would have been a legal code. Then r2 will be a reference to c1.
Now, As i stated reference is other name of an object. It means if we do arithmetic on r2 it will be done on ci.
r = r+2; // it means ci = ci + 2 ;
but see ci was const(it shouldn't be altered but we altered it.)
So we need to do something like.
const int ci = 1024;
const int &r2 = ci; // error: non-const reference to a const object
This ensures that we can't do
r = r +2; which ensures/preserves the constness of ci.
get() is returning const so to preserve it's constness the variable used to receive from function must be const. So, the part which you said is working is an error as per standard, may be it's your compiler extension.
Keep in mind that const X& essentially means that the compiler imposes restriction that you cannot mutate the value reference via the reference (as the page you linked says). The actual value referenced by the reference is purely determined at runtime and the compiler cannot infer that you are mutating the value referenced by x when it sees the line a = n * 2;.
A small change to your code can help what is going on,
#include <iostream>
using namespace std;
int a = 1;
int b = 1; // new variable
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
cout << "n: " << n << endl;
//f(); // Might change the value of whatever n refers to
}
int main()
{
// Whether `x` in `DoWork` refers to `b` or `a` is purely determined at runtime.
if (rand() % 2 == 0)
DoWork(a);
else
DoWork(b);
cout << "a: " << a << endl;
}
In other words, the compiler cannot be sure of what values references will point to. In C++, references can even point to invalid memory,
int *a = 0;
int &ref = *a;
cout << ref; // runtime error
Hope this helps.
Edit:
Regarding the followup question, what compiler are you using? Any standard compiler would refuse to compile the code.

Output ASCII value of character

#include <iostream>
using namespace std;
int main()
{
char x;
cout << "enter a character:";
cin >> x;
cout << "ASCII Value of " << x << "is" << string(x);
return 0 ;
}
the error is
main.cpp||In function 'int main()':|
main.cpp|10|error: invalid conversion from 'char' to 'const char*'|
main.cpp|10|error: initializing argument 1 of 'std::basic_string<_CharT, _Traits,_Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]'|
||=== Build finished: 2 errors, 0 warnings ===|
std::cout << "ASCII Value of " << x << "is" << (int)x;
is one way (the cast circumvents the special treatement of a char type by the I/O stream library), but this will output your platform's encoded value of the character, which is not necessarily ASCII.
A portable solution is much more complex: You'll need to encode the ASCII set in a 128 element array of elements capable of storing a 7 bit unsigned value, and map x to a suitable element of that.
There are 3 approaches to solving this problem:
Use to_string
Passing the correct value to cout
Using the std::string class correctly
The solutions are marked (numbers in comment).
Use std::to_string
Since C++11, there is function to convert numbers to a string (to_string):
/*(1)*/ std::cout << std::to_string( x );
There is no specialization for a char parameter. So the value is implictly converted.
Passing the correct value to cout
cout would display the value of char object as a character.
If we want to output the value of a char object, we need to convert it to a type which is output by cout as a number instead of a character.
The C++ standard guarantees:
1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
So any of those integer types can be used. Usually int is selected.
There are 4 conversions that can be used here:
1) Implicit - "Implicit conversions are performed whenever an expression of some type T1 is used in context that does not accept that type, but accepts some other type T2;"
/*(2)*/ int i = x;
std::cout << i;
2) Explicit - "Converts between types using a combination of explicit and implicit conversions."
/*(3)*/ std::cout << (int)x;
/*(4)*/ std::cout << int(x); // unsigned int(x) - is invalid,
// has to be a single-word type name
3) A named cast.
/*(5)*/ std::cout << static_cast<int>(x);
4) Use the T{e} notation for construction
/*(6)*/ std::cout << int{x};
The T{e} construction syntax makes it explicit that construction is desired. The T{e} construction syntax doesn’t allow narrowing. T{e} is the only safe and general expression for constructing a value of type T from an expression e. The casts notations T(e) and (T)e are neither safe nor general.
About conversions the C++ Core Guidelines specifies the following (among others)
ES.48: Avoid casts
ES.49: If you must use a cast, use a named cast
ES.64: Use the T{e}notation for construction
In this case I would suggest (3) or (4).
Using the std::string class correctly
string is a specialization of basic_string
using string = basic_string<char>;
basic_string has many constructors.
There are only 2 constructors, which can take a predefined number of chars;
basic_string( size_type count, CharT ch, const Allocator& alloc = Allocator() );
Constructs the string with count copies of character ch. The behavior is undefined if count >= npos.
/*(7)*/ std::string s = std::string( 1, x );
basic_string( const CharT* s, size_type count, const Allocator& alloc = Allocator() );
Constructs the string with the first count characters of character string pointed to by s. s can contain null characters. The length of the string is count. The behavior is undefined if s does not point at an array of at least count elements of CharT, including the case when s is a null pointer.
/*(8)*/ std::string s = std::string( &x, 1 );
#include <iostream>
using namespace std;
int main()
{
char x;
cout<< "enter a character:";
cin>>x;
cout<< "ASCII Value of "<< x<< "is"<< int(x);
return 0 ;
}
you mean return try this code
#include <iostream>
using namespace std;
int main()
{
char x;
cout<< "enter a character:";
cin>>x;
cout<< "ASCII Value of "<< x<< "is"<< char(x);
return 0 ;
}
try this its called return