2

I have the following code that does not compile with Qt 6.7.2 on Debian :

#include <QCoreApplication>

#include <iostream>    
#include <variant>

class Foo : public QObject
{
    Q_OBJECT
};

class Bar : public QObject
{
    Q_OBJECT
};

struct Vis
{
    void operator()(const Foo &f)
    {
        std::cout << "foo visitor" << std::endl;
    }
    void operator()(const Bar &b)
    {
        std::cout << "bar visitor" << std::endl;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    std::variant<Bar, Foo> v;
    Foo f;
    Bar b;

    v = b;
    std::visit(Vis{}, v);
    v = f;
    std::visit(Vis{}, v);

    return a.exec();
}

#include "main.moc"

The error is :

error: no match for ‘operator=’ (operand types are ‘std::variant<Bar, Foo>’ and ‘Bar’)
v = b;

As a workaround it seems ok to use raw pointers to QObject's instead :

struct Vis
{
    void operator()(const Foo *f)
    {
        std::cout << "foo visitor" << std::endl;
    }
    void operator()(const Bar *b)
    {
        std::cout << "bar visitor" << std::endl;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    std::variant<Bar *, Foo *> v;
    Foo *f;
    Bar *b;

    v = b;
    std::visit(Vis{}, v);
    v = f;
    std::visit(Vis{}, v);

    return a.exec();
}

Am i missing something to store a QObject derived class inside a std::variant?

7
  • 6
    "copy assignment operator of 'Bar' is implicitly deleted because base class 'QObject' has a deleted copy assignment operator". Commented Apr 30 at 13:38
  • 3
    You need to look at the full and complete error log. Commented Apr 30 at 13:41
  • 2
    The answer is in the complete error message. QObjects can not be copied. Commented Apr 30 at 13:44
  • Thanks, so the workaround is actually a solution if i am not mistaken. Commented Apr 30 at 13:47
  • 2
    why not just use virtual functions and polymorphism ? it would be faster. Commented Apr 30 at 13:48

2 Answers 2

7

QObject has no assignment, hence neither Foo nor Bar have an assignment operator. For the reasons I refer you to https://doc.qt.io/qt-5/object.html#identity-vs-value. This would fail with similar eror for the same reason:

Foo f;
Foo g;
f = g;

As Foo and Bar already share a common base you can make use of polymorphism. Rather than using std::visit use virtual functions:

struct foobarable { 
    virtual ~foobarable() {}
    virtual void foobar() = 0; 
};

class Foo : public QObject,foobarable
{
    Q_OBJECT
    void foobar() override { std::cout << "foo" << std::endl; }
};

class Bar : public QObject,foobarable
{
    Q_OBJECT
    void foobar() override { std::cout << "bar" << std::endl; }
};

As mentioned in a comment, if you really want to use std::variant<Foo,Bar> a workaround would be to use emplace to construct the instance in place:

std::variant<Bar, Foo> v;
v.emplace<Bar>();
std::visit(Vis{}, v);
v.emplace<Foo>();
std::visit(Vis{}, v);

Full example

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks but could you show an example of the workaround with std::variant and emplace in this case ? I am still struggling with it.
And as you mentioned the "Identity vs Value" discussion, QObject has identity semantics whereas std::variant has value semantics. So i am still thinking that a pointer to a QObjectis a quiet reasonable workaround too.
@kzsnyk see update.
4

This is documented in Qt,

QObject Class | Qt Core | Qt 6.9.0

No Copy Constructor or Assignment Operator

QObject has neither a copy constructor nor an assignment operator. This is by design. Actually, they are declared, but in a private section with the macro Q_DISABLE_COPY(). In fact, all Qt classes derived from QObject (direct or indirect) use this macro to declare their copy constructor and assignment operator to be private. The reasoning is found in the discussion on Identity vs Value on the Qt Object Model page.

Since your classes inherit QObject they do not have assign operator or copy constructor too.

Note that std::variant acquiesce this properties to based on properties of types it can hold.

You are trying to use converting assignment:

std::variant<Types...>::operator= - cppreference.com

  1. Converting assignment.
  • ....

This overload participates in overload resolution only if std::decay_t<T>(until C++20)std::remove_cvref_t<T>(since C++20) is not the same type as variant and std::is_assignable_v<T_j&, T> is true and std::is_constructible_v<T_j, T> is true and the expression F(std::forward<T>(t)) (with F being the above-mentioned set of imaginary functions) is well formed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.