Type conversion function

Keywords: C++ Programming

Catalog

1. Conversion constructor

Class constructors can define different types of parameters. When the parameters satisfy the following conditions, they can be called transformation constructors.

  • A function has only one parameter
  • Parameters are basic types or other types

Among them, there is a special case, which can also constitute a transformation constructor.

  • Functions have multiple parameters, but all but the first are default parameters
  • The first parameter is the basic type or other type.
  • Use only one parameter when calling a function

The C++ compiler will try to make the source code compile when compiling, so if it encounters such code Test t = 100, the compiler will not immediately report an error, but will try the following:

  • Find out if a transformation constructor is defined in a class
  • If Test(int i) is defined, Test(100) is invoked to implicitly convert int type to Test type, and then assigned to t, which is compiled and passed.
  • If there is no definition, compilation will report errors
#include <iostream>

using namespace std;

class Test
{
    int mValue;
public:
    Test()
    {
        mValue = 0;
    }

    //Conversion constructor
    Test(int i)
    {
        mValue = i;
    }

    //When invoked only with the first parameter, the function is equivalent to Test(int i) and is also a transformation constructor.
    /*Test(int i, int j = 0, int k = 0)
    {
        mValue = i;
    }*/

    Test operator + (const Test &p)
    {
        Test ret(mValue + p.mValue);

        return ret;
    }

    int value()
    {
        return mValue;
    }
};

int main()
{
    Test t = 5;      // Test t = Test(5);
    Test r = t + 10; // Test r = t + Test(10);

    cout << "t.value = " << t.value() << endl;
    cout << "r.value = " << r.value() << endl;

    return 0;
}

As you can see, when a transformation constructor is defined, the result of the compiler's best efforts is implicit type conversion, whereas implicit type conversion

  • It's possible to make programs work in unexpected ways.
  • It is an important source of BUG in engineering and should be avoided as far as possible.

2. explicit keywords

  • The explicit keyword can be used to modify the transformation constructor in engineering, thus eliminating the attempt of compiler transformation.
  • When the transformation constructor is modified by explicit, only explicit forced type conversions can be used
  • As a general principle of programming, it is recommended to add explicit keywords to all constructors.
#include <iostream>

using namespace std;

class Test
{
    int mValue;
public:
    explicit Test()
    {
        mValue = 0;
    }

    explicit Test(int i)
    {
        mValue = i;
    }

    //When invoked only with the first parameter, the function is equivalent to Test(int i) and is also a transformation constructor. explicit is valid and necessary
    /*explicit Test(int i, int j = 0, int k = 0)
    {
        mValue = i;
    }*/

    Test operator + (const Test &p)
    {
        Test ret(mValue + p.mValue);

        return ret;
    }

    int value()
    {
        return mValue;
    }
};

int main()
{
    //Test t = 5;      // Error
    //Test r = t + 10; // Error

    Test t = static_cast<Test>(5);
    Test r = t + static_cast<Test>(10);

    cout << "t.value = " << t.value() << endl;
    cout << "r.value = " << r.value() << endl;

    return 0;
}

When the explicit keyword is used, if main() replaces 43-44 lines with 40-41 lines, the compilation will report a direct error.

3. Type conversion function

Conversion constructors can convert other types to class types, while type conversion functions can convert class types to other types, including common types and other class types.

  • Type conversion functions are inverse processes of transformation constructors, and they have the same status.
  • The compiler can also use type conversion functions for implicit conversion, so as to try to get the source code through compilation.
  • When the target type is another class type, the type conversion function may conflict with the transformation constructor.

To define a type conversion function, you need to use the operator keyword, whose grammatical rules are

operator TargetType ()
{
    TargetType ret;
    //......
    return ret;
}

When the compiler encounters test t (1); int i = t; such code, it does not immediately report an error, but makes the following attempt

  • See if there is a type conversion function operator int () defined in the Test class
  • If there is a definition, the implicit conversion is performed. First, the type conversion function is invoked to convert t to int, and then assigned to i. Compilation is carried out through
  • If there is no definition, compilation will report errors
#include <iostream>

using namespace std;

class Test;

class Value
{
    int mValue;
public:
    Value(int i = 0)
    {
        mValue = i;
    }

    //If not explicit, it will conflict with operator Value () in Test, resulting in ambiguity.
    explicit Value(Test &t)
    {

    }

    int value()
    {
        return mValue;
    }
};

class Test
{
private:
    int mValue;
public:
    Test(int i = 0)
    {
        mValue = i;
    }

    int value()
    {
        return mValue;
    }

    operator int ()
    {
        return mValue;
    }

    operator Value ()
    {
        Value ret(mValue);

        return ret;
    }
};

int main()
{
    Test t(100);
    int i = t;
    Value v = t;

    cout << "i = " << i << endl;
    cout << "v.value = " << v.value() << endl;

    return 0;
}

Unlike the transformation constructor, the type conversion function does not have such an exclusion mechanism as explicit, that is to say, as long as the type conversion function is defined, we cannot suppress the implicit call of the compiler.
Therefore, in engineering, typesetting functions are not usually used, but public member functions of toType() instead of typesetting functions.

#include <iostream>

using namespace std;

class Test;

class Value
{
    int mValue;
public:
    Value(int i = 0)
    {
        mValue = i;
    }

    //If not explicit, it will conflict with operator Value () in Test, resulting in ambiguity.
    explicit Value(Test &t)
    {

    }

    int value()
    {
        return mValue;
    }
};

class Test
{
private:
    int mValue;
public:
    Test(int i = 0)
    {
        mValue = i;
    }

    int value()
    {
        return mValue;
    }

    /*
     * Unused and not recommended methods in Engineering
    */
    /*operator int ()
    {
        return mValue;
    }

    operator Value ()
    {
        Value ret(mValue);

        return ret;
    }*/

    /*
     * Commonly used and recommended in engineering: Provide public member functions for toType()
    */
    int toInt()
    {
        return mValue;
    }

    Value toValue()
    {
        Value ret(mValue);

        return ret;
    }
};

int main()
{
    Test t(100);
    int i = t.toInt();
    Value v = t.toValue();

    cout << "i = " << i << endl;
    cout << "v.value = " << v.value() << endl;

    return 0;
}

Posted by transparencia on Wed, 25 Sep 2019 05:44:13 -0700