operator Type* ()

Recommended Posts

Jiia    592
Is it possible to provide pointer conversion operators without having them automatically allow the [] operator? Here's an example:
class MyClass
{
public:

operator INT* () { return somedata; }
operator const INT* () const { return somedata; }

private:
INT *somedata;
CHAR *details;
};
Now it's possible to do this: MyClass instance; instance[0] = 1; But I only want it to do this: INT *ptr = instance; ptr[0] = 1; Or this (with VOID SomeFunc(INT* data, Blah)): SomeFunc(instance, blah); But why does the compiler assume it's okay to convert to an int pointer just by me using [] operator? Is it possible to create a conversion operator that only works when you're specifically asking for that type of data? Or do I need to provide a GetIntData() type function?

Share on other sites
Promit    13246
Out of curiosity, what happens if you give the class a [] operator, but make it private?

Share on other sites
Fruny    1658
No.

Promit - Ambiguity.

Share on other sites
Jiia    592
Actually, a private [] operator doesn't seem to cause any problems. But my goal was actually to define a different use for the [] operator. I don't want the conversion routine to do the same thing. But I guess I'm not allowed to define both at once in the public interface.

Thanks for the help.

Share on other sites
Fruny    1658
Quote:
 Original post by JiiaBut I guess I'm not allowed to define both at once in the public interface.

Private or public doesn't change the way names are looked up. Only whether you can access them or not. That is, making something private won't change the behaviour of the class by magically re-routing to a public function. Instead the private function will still be picked, but the compiler will tell you you don't have the right to use it. This is intentional.

Share on other sites
Promit    13246
I suggest that you get rid of the implicit pointer conversion entirely. It's dangerous, in more ways than one. If you need pointer-like behavior, do what standard library iterators do and override the * and -> operators. If you really, really need an actual pointer, make it an explicit function, like GetPointer().

Share on other sites
Jiia    592
The goal was just to make it simple to pass the object to functions which work with the generic data rather than forcing the function to accept a reference to the specific class.

Why do you say the conversions are dangerous? Is it unpredictable?

Share on other sites
Promit    13246
Couldn't you just use templates in that case? If you provide the * and -> operators, and pass to a templated function which uses those operators, it should achieve the same goal.

The reason implicit pointer conversions are dangerous is because, as you discovered, they introduce possibilities for a whole lot of new semantics, most of which are usually not meaningful for the class where the pointer came from. For more details on the matter, investigate why std::string isn't implicitly convertable to a const char*.

Share on other sites
Jiia    592
Quote:
 Original post by PromitCouldn't you just use templates in that case? If you provide the * and -> operators, and pass to a templated function which uses those operators, it should achieve the same goal.

Not when the functions are standard or library functions which I haven't actually written myself.

I'll try to find a way around using the conversion. I appreciate your time.

Share on other sites
Quote:
 Original post by JiiaThe goal was just to make it simple to pass the object to functions which work with the generic data rather than forcing the function to accept a reference to the specific class.Why do you say the conversions are dangerous? Is it unpredictable?

Typically the compiler will, whenever possible, handle automatic and implicit type conversions for overloaded operators. This is somewhat of a catch-22 in that if the implicit conversion is not done the usability of such overloading is degraded. If the type conversion IS done you can run into problems where the conversion wasn't what you intended. If MyClass has a type conversion to char* you may end up inadvertently passing a var of type MyClass to a function that accepts char*. What that function does with the pointer or it's contents may not be acceptable to MyClass. Since the conversion is implicit you are never altered to the exact nature of the operation (i.e. whether the conversion took place or not).

Using raw arrays rather than existing (or even custom) access containers introduces the possibility of buffer overflows which can cause data, heap, and stack corruption. Most of the code that you would write to access them AND make sure you don't exceed the boundries of the array is (in most cases) not going to be much different than using a container and associated iterators. Using an existing container and iterator gives you better code reuse and reduces/eliminates the possibility of causing a BoF. It's safer and typically just as efficient.

In your case the [] operator wasn't overloaded so the compiler automatically chose the next available and suitable conversion for the operation. Operators sich as [] and -> typically have a deeper amount of 'implicity' applied to make their behavior more closely resembler typical array and member access operations. By supplying an explicit [] operator you can make sure that the behavior and operations executed are exactly what you intend. Such as the following:

Note I left in the type conversions for int* only to show how the operations change when an explicit [] operator is included - I still consider them undesirabe
class MyClass{private:	class ArrayElementImmitation {	public:		void SetIndex(int index) {			std::cout << "Setting index to " << index << "\n";		}		void SetIndex(const char *index) {			std::cout << "Setting named index to " << index << "\n";		}		ArrayElementImmitation &operator=(int val) {			std::cout << "Do something spectacular with value " << val << "\n";			return *this;		}	};	int bucket[30];	int *somedata;	ArrayElementImmitation varray;public:	MyClass() {		somedata = bucket;	}	// Dangerous	operator const int* () const { return somedata; }	// Dangerous	operator int* () { return somedata; }	// using element containers. This returns a reference to	// varray which is interpreted as the element returned by a normal	// array operation.	ArrayElementImmitation &operator[](int index) {		varray.SetIndex(index);		return varray;	}	ArrayElementImmitation &operator[](const char *index) {		varray.SetIndex(index);		return varray;	}};class test {public:	test() {		MyClass a;		int value;		int *ptr;		ptr = a;		value = ptr[3];		ptr[3] = 0;		a[0] = 493;		a["hello"] = 392;	}} test;

Share on other sites
As a side note you might want to look through the code STL iterators, list, and strings and check our the various overloaded operators and how they related to method based operations and which methods don't have corresponding conversions (i.e. string's c_str() method vs. const char* type conversion). It's interesting to see the minor differences between common usage implementation and specific implementations of certain types such as iterators.