class NullClass{
public:
template<class T>
operator T*() const {return 0;}
};
I was reading Effective C++ and I came across this class, I implemented the class and it compiles. I have a few doubts over this:
It doesn't have a return type.
What is this operator.
and what it actually does.
That's the type conversion operator. It defines an implicit conversion between an instance of the class and the specified type (here T*). Its implicit return type is of course the same.
Here a NullClass instance, when prompted to convert to any pointer type, will yield the implicit conversion from 0 to said type, i.e. the null pointer for that type.
On a side note, conversion operators can be made explicit :
template<class T>
explicit operator T*() const {return 0;}
This avoid implicit conversions (which can be a subtle source of bugs), but permits the usage of static_cast.
Related
I am learning about templates in C++, and came across an example where casting to void is used:
template<typename T>
auto func (T const& t) -> decltype( (void)(t.size()), T::size_type() )
{
return t.size();
}
In the explanation it is written that:
The cast of the expression to void is to avoid the possibility of a user-defined comma operator overloaded for the type of the expressions.
My question(s) is/are:
How can a cast to void be used to "avoid the possibility of a user-defined comma operator overloaded for the type of the expressions"? I mean, can anyone give any example where if we don't use void then this code would give an error? For example, let's say we have a class called SomeClass which has overloaded the comma operator. Now, can this become a problem if we don't use void?
Can static_cast be used in this case instead of a C style cast? For example, something like static_cast<void>(t.size()). I am reading examples that use C++17 features, and so I wonder why the author has used a C style cast in this case.
I have read What does casting to `void` really do?, from which I get the impression that if we use (void)x then this means to suppress compiler warnings, and also means "ignore the value of x". But then I can't understand the difference between the expression x and (void)x.
Consider this pathological type:
struct foo {
struct size_type {
bool operator,(size_type) { return false;}
};
size_type size() { return {};}
};
It does have a size_type and it does have a size() method. However, without the cast to void, the template does not deduce the right return type, because decltype( (t.size()), typename T::size_type() ) is bool :
#include <type_traits>
template<typename T>
auto func (T const& t) -> decltype( (t.size()), typename T::size_type() )
{
return t.size();
}
struct foo {
struct size_type {
bool operator,(size_type) { return false;}
};
size_type size() const { return {};}
};
int main()
{
func(foo{});
}
Results in error:
<source>:6:8: error: no viable conversion from returned value of type 'foo::size_type' to function return type 'decltype((t.size()) , typename foo::size_type())' (aka 'bool')
return t.size();
^~~~~~~~
<source>:20:4: note: in instantiation of function template specialization 'func<foo>' requested here
func(foo{});
^
1 error generated.
ASM generation compiler returned: 1
<source>:6:8: error: no viable conversion from returned value of type 'foo::size_type' to function return type 'decltype((t.size()) , typename foo::size_type())' (aka 'bool')
return t.size();
^~~~~~~~
<source>:20:4: note: in instantiation of function template specialization 'func<foo>' requested here
func(foo{});
^
A static_cast can be used. However, as nothing is actually being cast (it is an unevaluated context), the c-style cast does not do much harm. Note how by using the cast to void the user defined operator, is bypassed and the correct return type is deduced: https://godbolt.org/z/jozx1YGWr. This is because void, whatever uses the built-in operator, whose result is of same type as whatever. You cannot override void, whatever with a user-defined operator,; there is no syntax that works.
I suppose the code is merely to illustrate this one effect, because even with the cast to void one can make up other examples that fail (eg the template does not explicitly test for t.size() actually returning T::size_type).
See also the section on "Rarely overloaded operators" here:
The comma operator, operator,. Unlike the built-in version, the overloads do not sequence their left operand before the right one. (until C++17) Because this operator may be overloaded, generic libraries use expressions such as a,void(),b instead of a,b to sequence execution of expressions of user-defined types. The boost library uses operator, in boost.assign, boost.spirit, and other libraries. The database access library SOCI also overloads operator,.
Consider the following class:
class foo
{
public:
constexpr operator bool() const noexcept;
constexpr operator void * &() noexcept;
constexpr operator void * const &() const noexcept;
constexpr operator void const * &() noexcept;
constexpr operator void const * const &() const noexcept;
};
A foo object would be used like this:
void bar(bool);
// ...
foo f;
bar(f); // error C2664: cannot convert argument 1 from 'foo' to 'bool'
// message : Ambiguous user-defined-conversion
// message : see declaration of 'bar'
The issue is that the operator bool is not considered because of its constness. If I make another function without the const qualifier, the problem is solved. If I make f const, the problem is solved as well. If I explicitly cast f to bool, the problem is solved.
Sample to work with
What are my other options and what is causing this ambiguity?
First you should have a look at this question regarding conversion precedence in C++.
Then as pointed by some comments, operator bool() was not selected by the compiler for this conversion and the ambiguous resolution message is not about it.
The ambiguity comes from constexpr operator void * &() noexcept versus constexpr operator void const * &() noexcept. The idea being: let's try to convert a non const object to something and then see if this something can be converted to bool.
Also operator void const*() and operator void*() are redundant because there is no situation when you can call one and not the other and you can get a const void* from a void*.
Also returning void const * const & does not have any advantage over returning void const* as you won't be able to modify the reference. Returning a pointer instead of a reference to a pointer at worst does not change anything, at best, prevent you from doing a double indirection.
My advice would be to remove the non const operators and replace them by an explicit setter when you want to change the underlying pointer stored in foo. But that might not be the solution you are looking for depending on your actual problem and your design choices.
Take a look at the following, simplified, code:
struct foo {
constexpr operator bool() const noexcept; // (1)
constexpr operator void * &() noexcept; // (2)
constexpr operator void const * &() noexcept; // (3)
};
void bar(bool);
// ...
foo f;
bar(f);
The implicit conversion rules state the following:
Order of the conversions
Implicit conversion sequence consists of the following, in this order:
zero or one standard conversion sequence;
zero or one user-defined conversion;
zero or one standard conversion sequence.
The conversion of the argument in bar is one user-defined conversion, followed by one standard conversion sequence.
Continued:
... When converting from one built-in type to another built-in type, only one standard conversion sequence is allowed.
A standard conversion sequence consists of the following, in this order:
zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion;
zero or one numeric promotion or numeric conversion;
zero or one function pointer conversion; (since C++17)
zero or one qualification adjustment.
There are planty of types that can be used where a bool is required, and pointers are amongst them, so for overloads #2 and #3 all that is required is one "lvalue-to-rvalue conversion". To use overload #1 the compiler will have to perform a "qualification adjustment" (const bool to bool).
How to fix this:
Remove ambiguity by adding the constexpr operator bool() noexcept;, which fits bar exactly.
What an operator is overloaded here?
operator T * ()
I know that the operator method has the following structure:
type operator operator-symbol ( parameter-list )
Assume we have the following code
template<typename T> class SmartPtr
{
public:
SmartPtr(T* data): member(data) {}
T* member;
T& operator * () { return *member; } //usage: *TObj
T*& operator () () { return member; } //usage: TObj()
operator T * () { return member; } //usage: ???
};
No compilation errors if you try it on the ideone. So what is going on here?
ADD: Am I right that static_cast<T*>(TObj) makes a call of the operator T *? I've tried it here.
That's a conversion operator, which allows the class to be converted to T*. Usage:
T * p = TObj;
It's probably a bad idea for a smart pointer to provide this, as it makes it easy to accidentally get a non-smart pointer. Standard smart pointers provide explicit conversion via a get() function instead, to prevent accidental conversions.
This operator is invoked when a SmartPtr object appears in an expression, but the compiler has no functions available to resolve the usage made thereof: if it's legal to use a T* where the SmartPtr appears, the operator T*() function is called to generate one. This means my_smartptr->my_T_member can work, as can f(T*); f(my_smart_ptr_to_T);. Similarly, iostreams have an operator bool() in C++11 (operator void*() in C++03 for reasons too tedious to bother with). It's kind of the opposite of an implicit constructor from a single parameter, where should the compiler not find a valid match using the parameter, it may try to construct an object using that parameter to use instead.
operator T * () { return member; }
it is a so-called conversion operator. It converts an object of type SmartPtr to type T *. Moreover it is an implicit conversion opertaor. So when the compiler awaits an object of type T * you can use instead an object of type SmartPtr.
You could make this conversion operator explicit if you would add keyword explicit. For example
explicit operator T * () { return member; }
I am using a library that is templated and that I do not wish to modify. Namely CImg.
This library has been mostly designed to work with templates of simple types: float, double, int etc.
At some point, this library does:
CImg<T>& fill(const T val) {
if (is_empty()) return *this;
if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
else std::memset(_data,(int)val,size()*sizeof(T));
return *this;
}
Now I want to use this library with a more complex class as template parameter. My particular class is such that sizeof(T)!=1 and most of the time, the fill function will properly assign val to each element with the proper operator= of my class. However, when !val, I would like a conversion operator that allows my class to be cast to an int and to produce some values (for example, 0 would make the function above work).
Right now, my program does not compile as it says:
error C2440: 'type cast' : cannot convert from 'const MyClass' to 'int'
How can I create an operator that allows for (int)my_variable with my_variable of type MyClass to be legal, without modifying the function above ?
Something like this using user defined conversions
int type;
explicit operator int()
{
return type;
}
What you want in this case is probably int conversion operator overload:
class A{
public:
explicit operator int() const{
return 2;
}
};
EDIT:
|I added explicit conversion that should make your code compile (at least the method you showed us), and not mess-up other operators, but it's only allowed since C++11, so if you are using older compiler it might not be available yet.
I saw the following code:
class NullClass {
public:
template<class T> operator T*() const { return 0; }
};
const NullClass NULL;
void f(int x);
void f(string *p);
f(NULL); // converts NULL to string*, then calls f(string*)
Q1> I have problems to understand the following statement
template<class T> operator T*() const { return 0; }
Specially, what is the meaning of operator T*()?
Q2> Why f(NULL) is finally triggering the f(string*)?
Thank you
what is the meaning of operator T*()?
It is a user-defined conversion operator. It allows an object of type NullClass to be converted to any pointer type.
Such conversion operators can often lead to subtle, unexpected, and pernicious problems, so they are best avoided in most cases (they are, of course, occasionally useful).
Why f(NULL) is finally triggering the f(string*)?
NULL is of type NullClass. It can't be converted to an int, but the user-defined conversion NullClass -> T* can be used to convert it to a string*, so void f(string*) is selected.
Note that this works because there is only one overload of f that takes a pointer. If you had two overloads,
void f(int*);
void f(float*);
the call would be ambiguous because the NullClass -> T* conversion can be converted to both int* and float*.
template<class T> operator T*() means that there is an implicit conversion from NullClass to T* for any T.
When you're calling f(NULL), the compiler needs to decide which overload to use. Since neither overload takes an object of type NullClass, it looks which implicit conversions exist. There is no conversion to int, but there is one to string*, so the conversion is applied and the string* overload is called.
operator Anything() overloads the "cast" operator. Whenever NullClass needs to be converted to Anything, this function will be called, and the result will be used.
In your case, Anything is T* where T can be any type (it is a template parameter), meaning a NullClass supports casting to any pointers.
Since NullClass can be cast into any pointers, including a string*. So the f(string*) version will be used.
Q1> I have problems to understand the following statement
template<class T> operator T*() const { return 0; }
Specially, what is the meaning of operator T*()?
It's an implicit conversion operator. It makes it possible to have objects of the type it belongs to implicit convert to the target type T* here. This version is a special one, since, being a template, it can convert an object of that NullClass to any pointer type.
Implicit conversion are frowned upon for good reasons. They have the bad habit of kicking in at unexpected moments, making the compiler call an unintended version of an overloaded function. Having a templatized implicit conversion operator is especially evil because templatization multiplies the possibilities.
Q2> Why f(NULL) is finally triggering the f(string*)?
See above. There is no possible conversion to int, so the implicit conversion operator kicks in and converts an NullClass object into any pointer requested.
I suppose this is intended. Usually you don't want a pointer to be converted to an integer, which is why that class has an implicit conversion into any pointer, but none to int.
That NullClass isn't all that bad, but the NULL instance of it is pure stupidity. As soon as you include any of the many headers that define the NULL macro (defined to be 0, i.e. an integer constant), the preprocessor will trample over all source and replace each usage of NULL with 0. Since you can't avoid to include this, this error renders the whole class pretty much useless.
The NuLLClass provides a conversion function to convert to a pointer. When you call f(NULL), it will try to find a way to convert NULL to a valid argument for f.
Because you can call operator T*() on NULL, it will with T=string. This fulfills the demand for f(string *). Since there is no way to convert NULL to an int, there is only one clear choice of which function to call.
Specially, what is the meaning of operator T*()?
It is a conversion operator to type T*. It allows operations such as (T*)NULL.
Q2> Why f(NULL) is finally triggering the f(string*)?
Because the compiler looks for the best match between the arguments and the method's signature, in this case choosing template<typename T> NullClass::operator T*(), where T=string.