So, if I have:
class test
{
public:
operator const int() {...}
operator int() {...}
};
Why is this following then ambiguous?:
test a;
(const int) a;
Isn't it like an explicit cast that I want a const int?
I tested this also with class types (not built in types) instead of int, and it is still ambiguous, but why?
Would be very happy if someone could explain why.
These are ambiguous.
operator const int() {...}l
operator int() {...}l
It's ambiguous because the return type isn't part of the what determines whether a method gets called and conversion types ignore const so two methods with the same signature aside will be ambiguous. However a different signature like this
operator const int() const {...}
operator int() {...}
will not be ambiguous and can be used to distinguish calls between const and non const objects. Further, this also means that the first function will not alter the object's "state" so is useful for users of the class in that it tells them the operation will not alter the object (aside from mutable members which can sometimes be useful for caching state.)
Please note that #user4581301's comment that there is rarely a reason to declare the return type const. It's going to get the type assigned to whether const or not. But this doesn't apply to reference returns.
As other's have pointed out, Conversion operators, unlike other methods, include the conversion type. But const is ignored so the following would not be ambiguous:
operator double() {...}
operator int() {...}
Related
What's the function of 'operator const GUID_t&() const' in the code snippet below.
It's quoted from a wellknown open source project,so i don't doublt the correctness.It does not look like ordenary operator overload,eg:CTest operrator(CTest&&), which you could clearly know the return type.Is there a term for this kind of usage?I would be grateful to have some help with this question.It would be better that if you could give a few such examples.
struct GUID_t{};
struct InstanceHandle_t
{
explicit operator const GUID_t&() const
{
return *reinterpret_cast<const GUID_t*>(this);
}
}
It's a user-defined conversion function of a general form:
operator T();
Here:
T = const GUID_t&
That is, it allows instances of InstanceHandle_t to be converted to const GUID_t& using the operations defined in the operator's body.
The additional explicit specifier is optional, and prevents implicit conversion, i.e., the compiler will trigger this conversion only in explicit contexts, such as:
InstanceHandle_t handler;
GUID_t guid(handler);
static_cast<GUID_t>(handler);
const GUID_t& ref(handler);
All the three statements result in executing:
*reinterpret_cast<const GUID_t*>(&handler)
Like other operators, invoking it directly is also possible:
handler.operator const GUID_t&();
This is an overloaded operator contained in a class:
inline operator const FOO() const { return _obj_of_type_FOO; }
I cannot for the life of me understand:
How I would invoke this operator?
What would be its return value?
[Secondary] Whether making it inline affects anything apart from efficiency?
That expression looks like a declaration of a conversion operator if Foo is a type and it is inside a class. The second const (the one closer to the opening curly bracket) means that the conversion can be called on const instances. Let us say the class is C. You can think of a conversion operator as a constructor outside a class. For example, you can't add constructors to the class std::string, but you can add a conversion operator to std::string to your classes. The result is that you can construct std::string from your class instance.
1) How to invoke the conversion operator: by constructing a value of type Foo from a C, for example:
Foo foo = c (where c is an instance of C, the class that declares the conversion operator). Mind you that the invocation of the conversion can happen implicitly. If you have, for example, void funOnFoo(Foo v); and an instace c of C, this might implicitly call operator const Foo: funOnFoo(c). Whether this actually does, depends on the usual things: Whether there are other overloads of funOnFoo, other conversions for C, etc.
2) The return value is const Foo
3) inline means the same thing as for any function, in particular, does not affect overload resolution
I overload an operator twice with the same parameter list. but with different return type:
T& operator()(par_list){blablabla}
const T& operator()(par_list){blablabla}
So when I'm calling the () operator, which function would be called based on what preference or situation? I know that if I call () under const function it has to be the const T& one.
I'm just curious how C++ deal with such situation and how the default preference works.
Thanks
These functions don't overload each other; they have the same signatures, and so the attempt to redefine the same function, which is an error. The return type is not part of a function's signature. To overload a function, you must declare a second function with the same name, but different parameters or const/volatile qualifiers - that is, qualifiers on the function, not the return type.
(They don't override each other either; overriding is what derived classes do to their base classes' virtual functions).
It's common to define a const and a non-const overload of a member function; the const overload must declare the function const, not just the return type:
T& operator()(par_list){blablabla}
const T& operator()(par_list) const {blablabla}
^^^^^
Now the first will be called if you apply () to a non-const object, and the second on a const object. For example:
Thingy nc;
Thingy const c;
nc(); // calls the first (non-const) overload
c(); // calls the second (const) overload
You can't overload a function/method based on return type. I would expect the compiler to throw an error here. What you can do is specify the method itself as a const method, using
const T& operator()(par_list) const {blahblah}
The const qualifier not only means this can be called on a const receiver, but it also is used in the overload resolution. This happens because it affects the implicit *this parameter that's passed to the method; a const method uses a const qualifier on *this, and const qualifiers are taken into account during overload resolution.
The way you define your operators, no way the compiler can decide which operator() to call. Overloading of functions (and operators) can only be done on the type of the arguments, never on the return type. And in fact, you will have an error at compilation as soon as you define the second one, the compiler considering that you are redefining the same function/operator.
However, the following is common (and probably what you have):
T& operator()(par_list){blablabla}
const T& operator()(par_list) const {blablabla}
This additional "const" after the argument list exists because you are defining non-static member functions and member functions have an implicit hidden argument: the "this" pointer to the instance of the class. The "const" keyword there indicates if this hidden pointer is to a const instance or not. This argument participates to the overloading resolution and that is in this case what the compiler use to choose which version of the operator to use.
So:
class A {
T& operator()() { ... }
const T& operator()() const { .... }
};
A a;
const A& ca(a);
a(); -> returns a T&
ca(); -> returns a const T&
Following is the test code:
struct A
{
operator int ();
operator int () const;
};
void foo (const int);
Now, upon invoking:
foo(A()); // calls A::operator int()
Why does it always chooses the non-const version ? Even making operator const int () const; doesn't have any effect on invoking foo(). Apart from standard reference, can someone explain logically, the reason behind it ?
A() gives you a temporary A object that is not const-qualified. The A() expression is an rvalue expression, yes, but that does not make the A object const-qualified.
Since the A object is not const-qualified, the non-const operator int() is an exact match and the const operator int() requires a qualification conversion, so the non-const overload is selected as the best match.
If you want it to be const-qualified, you'd need to explicitly request a const-qualified A:
foo(identity<const A>::type());
where identity is defined as
template <typename T>
struct identity { typedef T type; };
Note that there is really no difference between operator const int() const and operator int() const: the result is an rvalue and only class-type rvalues can be const-qualified (int is not a class type).
Note also that there is no difference between the void foo(const int) that you have and void foo(int). Top-level const-qualifiers on parameter types do not affect the type of the function (i.e., the type of both of those declarations is void foo(int)). Among other reasons, this is because it doesn't matter to the caller whether there is a top-level const-qualifier; it has to make a copy regardless. The top-level const-qualifier affects only the definition of the function.
James McNellis’ answer really covered it all, but it doesn’t hurt (I hope) with more explanations.
So.
When you call …
o.operator int()
… then the overload selection depends entirely on the constness of o.
Nothing else.
To see why, consider this class:
struct Bar
{
void f() {}
void f() const {}
};
Technically those member functions do not need to be member functions. They could just as well have been chosen to be free standing functions. But then they need Bar argument:
struct Bar
{};
void f( Bar& ) {}
void f( Bar const& ) {}
And hopefully now it's easier to see that when you do
Bar o;
f( o );
then the first function can be selected. And so it is. Because if the second function was selected, then you could never get the first one. Because if you make the object const, then it would break const correctness to select the first one. So when the object is const only the second can be selected, hence, when it is not const the first one is selected.
In short, the only practical alternative to this rule would be to always select the second one, which would make the first one rather useless, yes?
Cheers & hth.,
One rule you have to remember about C++: it never takes into account the value that is being returned when it selects an overload. In this case since the operator int function takes no parameters, it can't use the parameter list to narrow down the choices either. All it can use it the constness of the object that it's being called from. Since this is a new temporary object, it's not const, so it doesn't choose the const overload.
Can somebody tell me what precisely
operator std::string()
stands for?
It is a conversion operator that allows the object to be explicitly or implicitly casted to std::string. When such a cast occurs, the operator is invoked and the result of the cast is the result of the invocation.
As an example of an implicit cast, suppose you had a function that accepted type std::string or const std::string&, but not the given object type. Passing your object to that function would result in the conversion operator being invoked, with the result passed to the function instead of your type.
It is a cast operator. Any class that defines this type can be used anywhere a std::string is required. For instance,
class Foo {
public:
operator std::string() const { return "I am a foo!"; }
};
...
Foo foo;
std::cout << foo; // Will print "I am a foo!".
Cast operators are almost always a bad idea, since there is invariably a better way to achieve the same result. In the above case, you are better off defining operator<<(std::ostream&, const Foo&).