Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

operator T() not used in assignment

I am a bit confused by this. Lets assume I have a helper class Data

class Data
{
public:
    Data(const QVariant &value) : m_Variant(value) { }
    operator QString() const { return m_Variant.toString(); }

private:
    QVariant m_Variant;
};

then when I do this:

Data d("text");
QString str = d; //str becomes "text"

it works but when I continue to do:

Data d2("text2");
str = d2; //does not compile

it fails complaining:

ambiguous overload for 'operator=' (operand types are 'QString' and 'Data')
candidates are:
...
QString &operator=(const QString &);
QString &operator=(QString &&);
QString &operator=(const char*); <near match>
    no known conversion from Data to const char*
QString &operator=(char);

But even providing

operator const char*() const;

does not help. The message about conversion just disappears and the error remains the same. Is there a way to solve this other than adding

QString &operator=(const Data &data);

to QString or calling explicitly

str = QString(d2);

?

I am confused because the compiler clearly deduced correctly that the left operand is a QString and it is apparently trying to call conversion from Data to what would one of the QString's operator=s accept but even if such conversion is defined it still does not work.

EDIT: The problem seems to come from multiple definitions of different operator T() members. In this case operator int().

like image 457
Resurrection Avatar asked Sep 18 '15 12:09

Resurrection


People also ask

Which is assignment operator in C++?

“=”: This is the simplest assignment operator. This operator is used to assign the value on the right to the variable on the left. For example: a = 10; b = 20; ch = 'y';

Is assignment operator overloaded by default?

The assignment operator must be overloaded as a member function. This will call f1. operator=(f1), and under the simplistic implementation above, all of the members will be assigned to themselves.

What does the default operator do in C++?

If a class definition does not declare a parameterless constructor, a copy constructor, a copy assignment operator, or a destructor, the compiler will implicitly declare them. These are called default operators. A C-like struct has these default operators.

Why should we always return copy assignment operator?

If you return a reference, minimal work is done. The values from one object are copied to another object. However, if you return by value for operator= , you will call a constructor AND destructor EACH time that the assignment operator is called!!


1 Answers

I haven't got access to an online compiler with the Qt libraries, but here's what I pieced together from the public documentation of QString and QVariant:

#include <iostream>

struct QString 
{
    QString() = default;
    QString(QString const&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    QString &operator=(const QString &) { std::cout << __PRETTY_FUNCTION__ << '\n'; return *this; }

    QString(char const*)  { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    QString &operator=(const char*) { std::cout << __PRETTY_FUNCTION__ << '\n'; return *this; }

    QString(char)  { std::cout << __PRETTY_FUNCTION__ << '\n'; }    
    QString &operator=(char) { std::cout << __PRETTY_FUNCTION__ << '\n'; return *this; }    
};

struct QVariant
{
    QVariant(QString const&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    QVariant(char const*)  { std::cout << __PRETTY_FUNCTION__ << '\n'; }
};

struct Data
{
    Data(QVariant const&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    operator QString() const  { std::cout << __PRETTY_FUNCTION__ << '\n'; return QString(); }
    operator int() const { std::cout << __PRETTY_FUNCTION__ << '\n'; return QString(); }
};

int main()
{
    Data d("text");
    QString str = d;

    Data d2("text2");
    str = d2;
}

Live Example

The direct-initializations of d("text") and d2("text2"), convert char const* -> QVariant through the constructors

QVariant::QVariant(const char*)
Data::Data(const QVariant&)

The copy-initialization QString str = d elides a call to the QString copy constructor on a temporary QString object produced by

Data::operator QString() const

Now for the bug: the assignment of str = d2 tries to match the various assignment operators QString::operator= overloads to your Data argument. Once Data has multiple conversion operators (to QString and to int e.g.), you will get an overload resolution ambiguity because QString has both the regular assignment operator=(QString) as well as the operator=(char) (which can take int).

TL;DR there is only one route for construction: char const* -> QVariant -> Data. But there are two routes Data -> QString and Data -> int -> char that lead to a valid argument for the assignment operator.

Fix: use explicit conversion operators in your own code (or remove one or more of them).

like image 110
TemplateRex Avatar answered Nov 22 '22 12:11

TemplateRex