Does C++ have no pointer type? - c++

In C++, these two have no difference.
char *pC;
char* pC2;
I thought that the type char and char* have got to be different types and all the C++ programmers had to use this form char* p instead of char *p, because when you do char *p it doesn't seem to specify that the programmer is using a pointer to char.
However, when you take a look at this code below, which is the same as nullptr from C++0x, it seems that T doesn't recognize any pointer type.
const class{
public:
// T is char, not char* in this case.
// T* is char*, but I thought it would be char**
template <class T>
operator T*() const{
cout << "Operator T*() Called" << endl;
return 0;
}
template <class C, class T>
operator T C::*() const{
cout << "Operator C T::*() Called" << endl;
return 0;
}
private:
void operator&() const;
}AAA = {};
int main(){
// operator T*() const is called,
// but it's awkward,
// because it looks like T will be char* type, so T* will be char**
char* pC = AAA;
}
Thanks in advance!

You're correct that char and char* are different types. Whether programmers say char* p or char *p is irrelevant. Those are three separate tokens, and there can be as many or as few spaces between them as you want; it has no effect on the type of p.
When you initialize pC with AAA, the compiler needs to choose a conversion operator from AAA. Since it's initializing a char* variable, it wants operator char*. To make that work, the compiler needs to choose T = char. Since T is char, there's no way T* could be char**.
If you want T to be a pointer type, then use AAA in a context where a pointer-to-pointer type is expected, such as char**. Then T will be char*.

Writing char* p or char *p is just the same thing, both are pointers to a char.

Related

Shared pointer constness in comparison operator ==

I stumbled upon an unexpected behavior of a shared pointer I'm using.
The shared pointer implements reference counting and detaches (e.g. makes a copy of), if neccessary, the contained instance on non-const usage.
To achieve this, for each getter function the smart pointer has a const and a non-const version, for example: operator T *() and operator T const *() const.
Problem: Comparing the pointer value to nullptr leads to a detach.
Expected: I thought that the comparison operator would always invoke the const version.
Simplified example:
(This implementation doesn't have reference counting, but still shows the problem)
#include <iostream>
template<typename T>
class SharedPointer
{
public:
inline operator T *() { std::cout << "Detached"; return d; }
inline operator const T *() const { std::cout << "Not detached"; return d; }
inline T *data() { std::cout << "Detached"; return d; }
inline const T *data() const { std::cout << "Not detached"; return d; }
inline const T *constData() const { std::cout << "Not detached"; return d; }
SharedPointer(T *_d) : d(_d) { }
private:
T *d;
};
int main(int argc, char *argv[])
{
SharedPointer<int> testInst(new int(0));
bool eq;
std::cout << "nullptr == testInst: ";
eq = nullptr == testInst;
std::cout << std::endl;
// Output: nullptr == testInst: Detached
std::cout << "nullptr == testInst.data(): ";
eq = nullptr == testInst.data();
std::cout << std::endl;
// Output: nullptr == testInst.data(): Detached
std::cout << "nullptr == testInst.constData(): ";
eq = nullptr == testInst.constData();
std::cout << std::endl;
// Output: nullptr == testInst.constData(): Not detached
}
Question 1: Why is the non-const version of the functions called when it should be sufficient to call the const version?
Question 2: Why can the non-const version be called anyways? Doesn't the comparison operator (especially comparing to the immutable nullptr) always operate on const references?
For the record:
The shared pointer I'm using is Qt's QSharedDataPointer holding a QSharedData-derived instance, but this question is not Qt-specific.
Edit:
In my understanding, nullptr == testInst would invoke
bool operator==(T const* a, T const* b)
(Because why should I compare non-const pointers?)
which should invoke:
inline operator const T *() const
Further questions:
Why isn't it the default to use the const operator?
Is this because a function cannot be selected by the type of the return value alone?
=> This is answered in Calling a const function rather than its non-const version
So this question boils down to:
Why doesn't the default implementation of the comparison operator take the arguments as const refs and then call the const functions?
Can you maybe cite a c++ reference?
When there exists an overload on const and non-const, the compiler will always call non-const version if the object you're using is non-const. Otherwise, when would the non-const version ever be invoked?
If you want to explicitly use the const versions, invoke them through a const reference:
const SharedPointer<int>& constRef = testInst;
eq = nullptr == constRef;
In the context of Qt's QSharedDataPointer, you can also use the constData function explicitly whenever you need a pointer.
For the intended usage of QSharedDataPointer, this behavior is not usually a problem. It is meant to be a member of a facade class, and thus used only from its member functions. Those member functions that don't need modification (and thus don't need detaching) are expected to be const themselves, making the member access to the pointer be in a const context and thus not detach.
Edit to answer the edit:
In my understanding, nullptr == testInst would invoke
bool operator==(T const* a, T const* b)
This understanding is incorrect. Overload resolutions for operators is rather complex, with a big set of proxy signatures for the built-in version of the operator taking part in the resolution. This process is described in [over.match.oper] and [over.built] in the standard.
Specifically, the relevant built-in candidates for equality are defined in [over.built]p16 and 17. These rules say that for every pointer type T, an operator ==(T, T) exists. Now, both int* and const int* are pointer types, so the two relevant signatures are operator ==(int*, int*) and operator ==(const int*, const int*). (There's also operator ==(std::nullptr_t, std::nullptr_t), but it won't be selected.)
To distinguish between the two overloads, the compiler has to compare conversion sequences. For the first argument, nullptr_t -> int* and nullptr_t -> const int* are both identical; they are pointer conversions. Adding the const to one of the pointers is subsumed. (See [conv.ptr].) For the second argument, the conversions are SharedPointer<int> -> int* and SharedPointer<int> -> const int*, respectively. The first of these is a user-defined conversion, invoking operator int*(), with no further conversions necessary. The second is a user-defined conversion, invoking operator const int*() const, which necessitates a qualification conversion first in order to call the const version. Therefore, the non-const version is preferred.
Maybe this code will allow you to understand what happens:
class X {
public:
operator int * () { std::cout << "1\n"; return nullptr; }
operator const int * () { std::cout << "2\n"; return nullptr; }
operator int * () const { std::cout << "3\n"; return nullptr; }
operator const int * () const { std::cout << "4\n"; return nullptr; }
};
int main() {
X x;
const X & rcx = x;
int* pi1 = x;
const int* pi2 = x;
int* pi3 = rcx;
const int* pi4 = rcx;
}
The output is
1
2
3
4
If the const object (or reference to it) is casted, the const cast operator (case 3 and 4) is choosen, and vice versa.
This is because of how the expression testInst == nullptr is resolved:
Let's look at the types:
testInst is of type SharedPointer<int>.
nullptr is (for the sake of simplification) of type T* or void*, depending on the use case.
So the expression reads SharedPointer<int> == int*.
We need to have equal types to invoke a comparison operator. There are two possibilities:
Resolve to int* == int*.
This involves a call to operator int *() or operator int const *() const.
[citation needed]
Resolve to SharedPointer<int> == SharedPointer<int>
This involves a call to SharedPointer(nullptr).
Because the second option would create a new object, and the first one doesn't, the first option is the better match. [citation needed]
Now before resolving bool operator==(int [const] *a, int [const] *b) (the [const] is irrelevant), testInst must be converted to int*.
This involves a call to the conversion operator int *() or operator int const *() const.
Here, the non-const version will be called because testInst is not const. [citation needed]
I created a suggestion to add comparison operators for T* to QSharedDataPointer<T> at Qt Bugs: https://bugreports.qt.io/browse/QTBUG-66946

Function overloading for const char*, const char(&)[N] and std::string

What I want to achieve is to have overloads of a function that work for string literals and std::string, but produce a compile time error for const char* parameters. The following code does almost what I want:
#include <iostream>
#include <string>
void foo(const char *& str) = delete;
void foo(const std::string& str) {
std::cout << "In overload for const std::string& : " << str << std::endl;
}
template<size_t N>
void foo(const char (& str)[N]) {
std::cout << "In overload for array with " << N << " elements : " << str << std::endl;
}
int main() {
const char* ptr = "ptr to const";
const char* const c_ptr = "const ptr to const";
const char arr[] = "const array";
std::string cppStr = "cpp string";
foo("String literal");
//foo(ptr); //<- compile time error
foo(c_ptr); //<- this should produce an error
foo(arr); //<- this ideally should also produce an error
foo(cppStr);
}
I'm not happy, that it compiles for the char array variable, but I think there is no way around it if I want to accept string literals (if there is, please tell me)
What I would like to avoid however, is that the std::string overload accepts const char * const variables. Unfortunately, I can't just declare a deleted overload that takes a const char * const& parameter, because that would also match the string literal.
Any idea, how I can make foo(c_ptr) produce a compile-time error without affecting the other overloads?
This code does what is needed (except the array - literals are arrays, so you can't separate them)
#include <cstddef>
#include <string>
template <class T>
void foo(const T* const & str) = delete;
void foo(const std::string& str);
template<std::size_t N>
void foo(const char (& str)[N]);
int main() {
const char* ptr = "ptr to const";
const char* const c_ptr = "const ptr to const";
const char arr[] = "const array";
std::string cppStr = "cpp string";
foo("String literal");
//foo(ptr); //<- compile time error
// foo(c_ptr); //<- this should produce an error
foo(arr); //<- this ideally should also produce an error
foo(cppStr);
}
In order for your deleted function to not be a better match than the template function, so that string literals still work, the deleted function needs to also be a template. This seems to satisfy your requirements (though the array is still allowed):
template <typename T>
typename std::enable_if<std::is_same<std::decay_t<T>, const char*>::value>::type
foo(T&& str) = delete;
Demo.
In modern versions of the language you may create some custom type and user-defined literal that will create it, so that it will be possible to pass "this"_SOMEWORDS, but not just c string literal, chat pointer or char array.
It doesn't exactly satisfy your requirements to pass string literal but I think it's good enough, especially because it forbids also arrays

String literal vs const char* function overload

I have a function that I want to work for const char*'s but it only works for string literals because they are given a special rule to be allowed to initialize arrays. The second overload, foo(const char*) will be preferred for both string literals and const char*s, but my template overload will not work for const char*s.
// Errors for const char*.
template<typename T, size_t n>
void foo(const T (&s)[n])
{
}
// Selected both times if both overloads are present.
void foo(const char*)
{
}
int main()
{
foo("hello");
const char* s = "hello";
foo(s); // Error if foo(const char*) is absent.
}
Is there a way to allow const char*s to initialize arrays?
I've tried this:
template<typename T, size_t n>
void _foo(const T (&s)[n])
{
std::cout << __PRETTY_FUNCTION__;
}
#define _expand(s) #s
#define expand(s) _expand(s)
void foo(const char* s)
{
_foo(expand(s));
}
I think that
const char* s = "hello";
is a pointer to the string literal somewhere in read only memory, and compiler cannot deduce the array size, so it chooses second overload.
You can use
const char s[] = "hello";
No, you can't initialize an array with a pointer. The string literal syntax is a special shorthand for a const char s[] and your working code roughly equivalent to
static const char s[]{'h','e','l','l','o','\0'};
foo(s);
So, you can pass arrays to your template function, including string literals, but you cannot pass pointers, including pointers to string literals.
On the other hand, arrays can decay to pointers, which is why both arrays and pointers can be passed to your second overload.

char pointer to array conversions to char pointer to pointer

I have a function:
void add(char const**);
And I invoke it as follow:
template<typename T>
void anotherAdd(T const& t) {
add(&t);
}
...
anotherAdd("some string");
As a result I get the error:
no known conversion for argument 1 from 'const char (*)[10]' to 'const char**'
Why the conversion can't be made?
Because I think the following is true:
"some string" <=> char const* =>
&"some string" <=> char const**
Arrays are not pointers.
This code expects a pointer to a pointer
void add(char const**);
The compiler is telling you that it can't produce a pointer to a pointer because your code has no pointer to point to. You're effectively trying to evaluate &"some string", which has no valid meaning.
This code will work, because it creates the missing char const* that you're trying to take the address of.
template<typename T>
void anotherAdd(T const& t) {
char const *pt = &t; // Now there's a pointer that you can take the address of.
add(&pt);
}

Operator overloading c++ (<<)

This the below program i have written for some test.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
void operator<<(const char *str1,const char *str2)
{
tgsetlist.insert( std::map<std::string,std::string>::value_type(str1,str2));
}
};
int main()
{
tgsetmap obj;
obj<<("tgset10","mystring");
obj.tgsetlist.size();
}
This throws a compilation error:
"test.cc", line 10: Error: Illegal number of arguments for tgsetmap::operator<<(const char, const char*).
"test.cc", line 22: Error: The operation "tgsetmap << const char*" is illegal.
2 Error(s) detected.*
Am i wrong some where?
You can't force operator<< to take two arguments on right-hand side. The following code:
obj<<("tgset10","mystring");
does not work as a function call with two arguments but instead just uses the , operator. But it's probably not what you are interested in.
If you need to pass two arguments to the << operator, you need to wrap them in some other (single) type. For example, you could use the standard std::pair, i.e. std::pair<const char*, const char*>.
But note that the operator<< should also return some reasonable type suitable for << chaining. That would probably be a tgsetmap& in your case. The following version should work fine:
#include <map>
#include <string>
#include <iostream>
class tgsetmap
{
public:
typedef std::map<std::string, std::string> list_type;
typedef list_type::value_type item_type;
list_type tgsetlist;
tgsetmap& operator<<(item_type item)
{
tgsetlist.insert(item);
return *this;
}
};
int main()
{
tgsetmap obj;
obj << tgsetmap::item_type("tgset10","mystring")
<< tgsetmap::item_type("tgset20","anotherstring");
std::cout << obj.tgsetlist.size() << std::endl;
}
Note that I've added typedefs to not have to repeat the type names over and over again. I've also made operator<< return a tgsetmap& so that << could be chained (used like in the modified main() above). And finally, I've reused the std::map<...>::value_type to make it simpler but you could also use any other type of your own.
But I believe that you may prefer using a regular method instead. Something like:
void add(const char *str1, const char *str2)
{
tgsetlist.insert( std::map<std::string, std::string>::value_type(str1, str2));
}
(inside the class declaration), and then:
obj.add("tgset10", "mystring");
The operator<< inside of a class must be overloaded like this:
T T::operator <<(const T& b) const;
If you want to overload it with 2 arguments, you can do it outside of a class:
T operator <<(const T& a, const T& b);
My compiler, for example, gives a more detailed error message for the code you posted:
If you are not sure about an operator overloading syntax, there is a wiki article about it.
Yes. operator << is binary operator. not ternary. not forget about this pointer.
As mentioned, the << is binary operator, so there is no way it can take more than two args(One should be this if you are declaring inside the class or a LHS if you are declaring outside the class). However you can accomplish the same functionality by doing obj<<"tgset10". <<"mystring";. But since << is a binary operator, you have to do some hack for this.
For this, I ve assigned a static variable op_count, where in I will determine if it is the value or the type. And another static variable temp_str to store the previous value across invocations.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
static int op_count = 0;
static const char *temp_str;
tgsetmap& operator<<(const char *str)
{
op_count++;
if (op_count%2 != 0) {
temp_str = str;
}
else {
tgsetlist.insert( std::map<std::string,std::string>::value_type(temp_str,str));
}
return this;
}
};
So you can do
int main()
{
tgsetmap obj;
obj<<"tgset10"<<"mystring";
obj.tgsetlist.size();
}
Or simply you can embed the value and type in the same string using some separator,
value:type = separator is :
value_type = separator is _.