const std::string::operator _char_type*() const

Started by
8 comments, last by Mxz 18 years ago
I don't think that std::string has this implemented... Although it would be useful... Is it possible to implement it from the outside? Something like:

const operator char* const (const std::string &String) {
    return String.c_str();
}

int main() {
    std::string String;

    char *pcString = String;
}

Thanks!
Advertisement
Accoring to GCC:

Quote:
`operator char*(const std::string&)' must be a nonstatic member function


And you have to write the const after the "operator" keyword:

operator const char* const (const std::string &String)
Quote:Original post by rip-off
Accoring to GCC:

Quote:
`operator char*(const std::string&)' must be a nonstatic member function


And you have to write the const after the "operator" keyword:

operator const char* const (const std::string &String)


Thanks...

Oh and I just learned a good lesson... Fire up GCC and actually try it [grin]. I'll do that next time.
It isn't implemented because std::basic_string<T> has the c_str function which does the same. Having an implicit conversion operator would introduce many functions, for example you couldn't use a string as an argument to functions which take both const char* and std::string. Also since it's converted to a const char* we want to explicitly show it. For example you have made an error in your example, pcString is non-const but String will be converted to a const char*.
Quote:Original post by CTar
It isn't implemented because std::basic_string<T> has the c_str function which does the same. Having an implicit conversion operator would introduce many functions, for example you couldn't use a string as an argument to functions which take both const char* and std::string. Also since it's converted to a const char* we want to explicitly show it. For example you have made an error in your example, pcString is non-const but String will be converted to a const char*.


Well... I'd just change pcString to const...

Quote:
Having an implicit conversion operator would introduce many functions, for example you couldn't use a string as an argument to functions which take both const char* and std::string.


Well... If you have an opportunity to use std::string I don't see a reason to have a function that accepts both... [grin]

<offtopic>And besides, why do ifstream and ofstream only accept char*s??? (And not strings.)</offtopic>
Quote:Original post by agi_shi
Well... I'd just change pcString to const...

Of course, but you introduced an error which was hard to spot before actually compiling because your String object was non-const so why shouldn't you be able to assign it to another type of non-const string?

Quote:Original post by agi_shi
Well... If you have an opportunity to use std::string I don't see a reason to have a function that accepts both... [grin]

Perhaps for people calling you functions from C, or if you are moving from char* to std::string but wants to be backwards-compatible. Generally it would be a bad idea, but AFAIK streams accept both char* and std::string so I don't think you could use std::string with streams (cout, stringstream etc.).

Quote:Original post by agi_shi
<offtopic>And besides, why do ifstream and ofstream only accept char*s??? (And not strings.)</offtopic>


I have no idea, I really don't like those classes. Also I think they are the only classes in the C++ Standard Library which accepts char, but not wchar_t (in the open function and their constructor).
Quote:Original post by agi_shi
<offtopic>And besides, why do ifstream and ofstream only accept char*s??? (And not strings.)</offtopic>


Because what they need is a pointer to a memory location, not a string. char is The Basic Type in C++.

Quote:Original post by CTar
It isn't implemented because std::basic_string<T> has the c_str function which does the same. Having an implicit conversion operator would introduce many functions, for example you couldn't use a string as an argument to functions which take both const char* and std::string. Also since it's converted to a const char* we want to explicitly show it. For example you have made an error in your example, pcString is non-const but String will be converted to a const char*.


It would also introduce some funny things: what would this code do:
std::string my_string("some value");if ("some value" == my_string) {  // ...}

If the implicit cast is defined, are you sure that operator==(const char*, const std::string&) will be called? Or will the string be casted to a const char* and then compared to the pointer?

It is good to generally avoid implicit cast operators in your class - side effects can really be weird.

Regards,
Quote:Original post by Emmanuel Deloget
Quote:Original post by CTar
It isn't implemented because std::basic_string<T> has the c_str function which does the same. Having an implicit conversion operator would introduce many functions, for example you couldn't use a string as an argument to functions which take both const char* and std::string. Also since it's converted to a const char* we want to explicitly show it. For example you have made an error in your example, pcString is non-const but String will be converted to a const char*.


It would also introduce some funny things: what would this code do:
std::string my_string("some value");if ("some value" == my_string) {  // ...}

If the implicit cast is defined, are you sure that operator==(const char*, const std::string&) will be called? Or will the string be casted to a const char* and then compared to the pointer?

I may be wrong, but as far as I understand it, the compiler is required to pick the overloaded function that makes least amount of conversions and if there are two or more overloaded functions that have the smallest amount of conversions, the function call is ambiguous and is a compile error.

The example you gave will, if that's true, call operator==(const char*, const std::string&) because this call is an exact match. Calling operator==(const char*, const char *) requires at least one conversion which is more more that zero conversions.

So the function called is either completely deterministic (assuming you are aware of all possible conversions and overloads, but that's a documentation issue), or the code fails to compile in the first place.

Quote:Original post by Emmanuel Deloget
It is good to generally avoid implicit cast operators in your class - side effects can really be weird.

Regards,

That I agree very much on though.
Quote:Original post by Brother Bob
Quote:Original post by Emmanuel Deloget
It would also introduce some funny things: what would this code do:
std::string my_string("some value");if ("some value" == my_string) {  // ...}

If the implicit cast is defined, are you sure that operator==(const char*, const std::string&) will be called? Or will the string be casted to a const char* and then compared to the pointer?

I may be wrong, but as far as I understand it, the compiler is required to pick the overloaded function that makes least amount of conversions and if there are two or more overloaded functions that have the smallest amount of conversions, the function call is ambiguous and is a compile error.

The example you gave will, if that's true, call operator==(const char*, const std::string&) because this call is an exact match. Calling operator==(const char*, const char *) requires at least one conversion which is more more that zero conversions.

So the function called is either completely deterministic (assuming you are aware of all possible conversions and overloads, but that's a documentation issue), or the code fails to compile in the first place.


The example I gave was more about code readability than code behavior - hence the questions, which really asks "can you understand what the code does without the need of a pencil, a paper, and some long thinking time ?".

The "minimum number of conversion" rule is fine when you have one or two conversion. If you have 4, 5, 6 coonversions in a row, it will be trickier to understand what's going on (of course, the strict application of the Holy One will give you the answer... if you know your Holy One by heart...). Plus, you may forget to count some trivial conversions (int to long, for example).

Regards,
Another interesting example of why the implicit conversion to const char* from a string class can cause subtle problems is the following.

#include<cstddef>#include<cassert>class string{public:static const unsigned int buffer_size = 200;char& operator[](unsigned int index){  assert(index < buffer_size);  return buffer_[index];}private:char buffer_[buffer_size];};int main(){string example;example[0] = 'a';}


Here we simply want to initialize the first letter of a fixed size string with the letter a. The code is fine, the string class has an overloaded operator[] which takes an unsigned integral type. We are calling that function with a signed integer literal (0), which has a standard conversion available to an unsigned integer, and so the function can be called after our signed integer undergoes this conversion.

However, if we then add an implicit conversion to a const char* to our string class, like so.

#include<cstddef>#include<cassert>class string{public:static const unsigned int buffer_size = 200;char& operator[](unsigned int index){  assert(index < buffer_size);  return buffer_[index];}operator const char*(){  return buffer_;}private:char buffer_[buffer_size];};int main(){string example;example[0] = 'a';}


The code now fails to compile.

The reason for this is that we have introduced another potential candidate for the call to an operator[], namely that of the operator[] applied to a const char*. The example string can now be converted to a const char* through our user defined conversion, and the operator[] can be called on that pointer without any further conversions. In addition, we have our original operator[] which can be called if the signed integer literal 0 is converted to an unsigned integer.

So we have two potential candidates available which both require one implicit conversion, so we have an ambiguity.

This can be solved in two ways, one would be to change our strings operator[] to take a signed int as an indexing parameter, like so

class string{//...  char& operator[](int index)  {   return buffer_[index];  }};


No implicit conversion is now required to call this operator with a signed int, and so there is no ambiguity with a signed int. But we have now introduced an ambiguity with an unsigned int. If the user then tries to do something like the following, the code will again fail to compile.

#include<cstddef>#include<cassert>class string{public:static const int  buffer_size = 200;char& operator[](int index){  assert(index < buffer_size);  return buffer_[index];}operator const char*(){  return buffer_;}private:char buffer_[buffer_size];};int main(){  string example;  for(unsigned int iter = 0; iter < 200; ++iter)  {    example[iter] = 'a';  }}


Here an implicit conversion is required to call both the const char* operator[] and our example strings operator[], namely the conversion from string to const char* and the conversion from int to unsigned int.

The solution to this would be to provide overloaded operator[] functions which take all possible indexing types as parameter, be it int, unsigned int, short int, unsigned short int, char, unsigned char or unsigned long int.

class string{public:char& operator[](unsigned int index);char& operator[](int index);char& operator[](unsigned short int index);char& operator[](short int index);char& operator[](unsigned char index);char& operator[](unsigned long int index);private:char buffer_[buffer_size];}


That should remove most ambiguities, but is far from ideal. You would also need the const member function counter parts of those functions so that they can be called on a constant string instance.

Quite an interesting and subtle problem though.

This topic is closed to new replies.

Advertisement