Reputation: 151
That is something that always intrigued me with the C++ language. With the appearance of the "const" identifier, it was supposed to replace the use of #define statement to declare constants. But as I stated to use them, I ended up in a const char to char conversion error madness.
Recently, I stated doing in class constants like done in Java. So I decided to declare something like this.
private: const static int NB_FIELD = 22;
This works perfectly. A function returning a int can return a "const int" without any problems.
But now if I try to do the same with a string like this:
private: static const char ERRORSTR [ 6 ];
Which constains the string "Error", it will generate const char to char conversion problems like in this function.
char* EnhancedSQLobject::get_SQLname ( int index )
{
if ( index < p_nb_field && index >= 0 )
return ( p_SQLfield_ptr [ index ] . SQLname)
return ( ERRORSTR );
}
Now some people will say change the return value for const char* but I can't because if the index is valid, it will return a non-const variable, but if the index is invalid, it will return a constant error string.
So my choice are either to make the error string non-constant, or create a constant using #define statements. I remember a long time ago trying to define everything as a const char, but eventually it blew up somewhere else in the chains of conversion. Making it just easier to use regular char (the String class is not a option for me)
This is plain ridiculous, why implement a feature that only works for only a part of the variable types. I imagine it's related to the fact that stings are in fact table of characters so it probably return a pointer instead of a value.
Upvotes: 1
Views: 1408
Reputation: 9770
Just so you know, using a #define
will not work as you expect it will, at least not if you're truly using C++ and not C.
In C++, a string literal is const
, thus if you declare:
#define ERRORSTR "ERROR"
And you try to return ERRORSTR
from get_SQLname
, it may cause undefined behavior if you try to modify the returned string. It's quite likely that your compiler is only issuing a warning for this because this fact that string literals are const
was not always true in C++ a long time ago.
#include <iostream>
#define BLAH "blah"
char *foo() {
return BLAH;
}
int main(int argc, char *argv[]) {
std::cout << foo() << std::endl;
}
On my compiler, I see the following warning:
so.cpp:6:12: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
The reality is that using #define
for constants, especially string constants is unsafe for exactly this reason, and thus should be avoided. const
provides you with some level of safety by the compiler, and if the compiler is telling you that the conversion is unsafe, there is a reason for that error, and you should try to figure out what that reason is and solve the problem instead of just trying to silence the error or find some other workaround. In fact, relying on behavior from C is generally a code smell in C++ and relying on the semantics of another language like Java is even worse. Despite their similarities in syntax, Java, C, and C++ are all very different from each other in their actual semantics. It would be best for you to learn those differences instead of just trying to apply the knowledge you know from one language to another.
Upvotes: 0
Reputation: 110658
In one case you are talking about casting const int
to int
, while in the other you are casting const char*
to char*
. These really are different situations that should behave differently, because the latter has a level of indirection added (the pointer). You absolutely can cast a const char
to a char
, but that's not what you're doing.
Consider the following erroneous code:
const int i = 50;
int j = i;
int* p = &i; // ERROR
I attempt to do int j = i;
and that is fine. The original i
object remains const
and the j
object is given the same value as i
. However, if I attempt to do int* p = &i;
, we have a problem. If this would compile, we would have p
pointing at a const int
, even though it tells us its pointing at a non-const int
. This would allow us to modify the const object i
through the pointer p
- that's very bad.
You have the same situation here, just with char
s. All of the char
s in your ERRORSTR
are const
. You can't then return a char*
that points at any of those char
s, because that would be lying. The pointer isn't pointing at a non-const char
, but at a const
char.
int
s and char
s don't behave any differently in this respect.
However, I think you've only entered this predicament because of poor interface design. Why does your function return a special string when it can't find the "SQLname" (whatever that is)? What if, for example, the string "Error" was actually a valid name? How would the caller of the function tell the difference? I'd argue that if your function isn't able to fulfill the action of get_SQLname
(that is, it isn't able to get the name), it should throw an exception.
On top of this, you should be using std::string
instead of C-style char*
s.
Upvotes: 2
Reputation: 302827
There's a difference between the two conversions. For int
:
const int ci = 7;
int i = ci; // fine...
++i; // ... because ci is still 7
But if you could do it for char*
:
const char msg[] = "Hi";
char* p = msg; // not OK...
*p = 'B'; // ... because now msg = "Bi"
// and it was supposed to be const!
The actual equivalent to the former for pointers would be:
char * const cc = someBuf();
char * c = cc; // this is totally OK
++c; // ... because we're not modifying cc in any way
It's the difference between a const pointer and a pointer to const.
Upvotes: 0