Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implicit conversions from and to class types

I am studying converting constructors and conversion operators in C++. What I've learned so far is that any non-explicit constructor that takes only one parameter (and any number of optional default arguments) represents an implicit class-type conversion to THAT class type, for example if a class defines a constructor that has one parameter of type int I can use an int wherever an object of that class type is required:

(assuming class_type has an overloaded += operator)

class_type a;
a+=5;

in this case 5 is implicitly converted (through the converting constructor) to class_typeand the overloaded operator is called.

Now, the (at least for me) tricky part: I know I can define a conversion operator as a member function :

operator int() {....};

that converts the object of class_type to the primitive int type, and I can use that conversion like:

class_type a;
a+5;

in this case I've read that the object is converted to an int through its conversion operator and then the buil-in sum operator is called. But what if I defined an overloaded + operator to take two class_type objects as its arguments? something like

class_type operator+(const class_type&,const class_type &c);

how is the compiler supposed to know which one to call through function matching? does the conversion to int only happens implicitly when only the built-in operator is defined?

thanks!

edit:

actually,I've tried to write some code to effectively try it out, it turned out that my compiler (g++) doesn't issue any ambiguous call error!

this is the class header (along with the non-memeber operator+ function declaration) :

#include <iostream>

class wrapper {
    friend std::ostream &operator<<(std::ostream&,const wrapper&);
  public:
    wrapper()=default; 
    wrapper(int);
    int get();
    operator int() const;
    wrapper operator+(int);
  private:
    int a=10;
};

std::ostream &operator<<(std::ostream&,const wrapper&);

and this is the main code:

#include "wrapper.h"

int main()
{
  using namespace std;
  wrapper w1;
  wrapper w2(5);
  cout<<w1<<" "<<w2<<endl;
  w1+1;  
}

now,I've defined a converting constructor from int to wrapper AND a conversion operator from class type to int(I've also overloaded the << output operator in order to print some results), but when the compiler evaluates the expression w1+1 it seems to be fine. How could it possibly be??

like image 209
Luca Avatar asked Aug 18 '15 09:08

Luca


2 Answers

If you have for example the following class declaration that contains a conversion constructor and a conversion operator

struct A
{
    A( int x ) : x( x ) {}
    operator int() const { return x; }
    int x;
};        

const A operator +( const A &a1, const A &a2 )
{
    return A( a1.x + a2.x );
}

then statement

a1 + a2;

where a1 and a2 are declared like for example

A a1( 10 );
A a2( 20 );

will be well-formed because there is no need to call a conversion function. The both operands match the parameter declarations of the operator +.

However if you will write for example

a1 + 20;

when the compiler issues an error because there is an ambiguity. The compiler can either apply conversion constructor A( int ) to convert the second operand to type A and call the operator defined for objects of type A. Or it can apply the conversion operator operator int to convert the first operand to type int and call the built-in operator + for objects of type int.

To avoid this ambiguity you could declare either the constructor or the operator (or the both) with function specifier explicit.

For example

    explicit A( int x ) : x( x ) {}

or

    explicit operator int() const { return x; }

In this case only one implicit conversion would exist and there was not an ambigiuty.

I would like to append the above description that sometimes some converion operators can be called implicitly even if they are declared with the function specifier explicit.

For example According to the C++ Standard (6.4 Selection statements)

  1. ...The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch;

and (5.16 Conditional operator)

1 Conditional expressions group right-to-left. The first expression is contextually converted to bool (Clause 4).

So for example if the above class has the following conversion operator declared with the function specifier explicit

explicit operator bool() const { return x != 0; }

nevertheless it will be called implicitly for example in the following statement

A a( 10 );

std::cout << ( a ? "true" : "false" ) << std::endl;

Here a will be converted to an object of type bool in the conditional operator.

EDIT: After you updated your question this expression

w1+1; 

is an exact match for operator

wrapper operator+(int);

Neither conversion are required. So the code compiles successfully.

like image 74
Vlad from Moscow Avatar answered Nov 08 '22 14:11

Vlad from Moscow


This is something you can easily try and see what the compiler does:

#include <iostream>

struct ABC {
    int v;
    ABC(int x) : v(x) { }
    operator int() const { return v; }
    void operator +=(ABC const &that) {
        v += that.v;
    }
};

ABC operator+(ABC const &lhs, ABC const &rhs) {
    return { lhs.v + rhs.v };
}

int main() {
    ABC a(5);
    std::cout << a + 1 << '\n';
    a += 10;
    std::cout << a << '\n';
}

what if I defined an overloaded + operator to take two class_type objects as its arguments?

GCC

error: ambiguous overload for 'operator+' (operand types are 'ABC' and 'int')

The compiler sees two candidates: operator+(int, int) <built-in> and ABC operator+(const ABC&, const ABC&). This means it could implicitly convert not only the 5 in a + 5 to a but also the a to int. Post these conversions both operator+ functions become potential matches.

How is the compiler supposed to know which one to call through function matching?

It doesn't know hence the error.

does the conversion to int only happens implicitly when only the built-in operator is defined?

Yes, otherwise it doesn't automatically convert class_type to int. However, int to class_type would happen implicitly unless you make class_type's constructor explicit:

explicit ABC(int x) : v(x) { }

If you've access to C++11, then you also make the conversion function explicit:

explicit operator int() const { return v; }
like image 35
legends2k Avatar answered Nov 08 '22 16:11

legends2k