I need to create a queue of different class objects (These classes are not related). I found a solution as follows:
Create a base class and use polymorphism. Here is how I implemented it,
class Task {
    public:
    virtual void operator()() {
        printf("should not be called\n");    
    } 
};
class TaskRPCB : public Task {
    private:
        int x;
        // other varibles
        std::function<void(int)> func;
    public:
        TaskRPCB(std::function<void(int)>&f , int x) {
            this->func = f;
            this->x = x;
        }
        void operator()() {
            printf("TaskRPCB function is executing...\n");
            func(x);
        }
};
class TaskECB : public Task {
    private:
        // other varibles
        std::function<void(void)> func;
    public:
        TaskECB(std::function<void(void)>&f) : func(f) {}
        void operator()() {
            printf("TaskECB function is executing...\n");
            func();
        }
};
void F1() { // dummy function for example
    cout <<"no x"<<endl;
}
void F2(int x) { // dummy function for example
    cout <<"x : "<<x<<endl;
}
int main() {
    queue<unique_ptr<Task>> Q;
    function<void()>    func1   = F1;
    function<void(int)> func2   = F2;
    TaskECB task1(func1);
    TaskRPCB task2(func2,4);
    Q.emplace(new TaskECB(func1));
    Q.emplace(new TaskRPCB(func2,4));
    (*Q.front())();
    Q.pop();
    (*Q.front())();
    Q.pop();
}
The problem is, I can not push the objects directly as shown above. I have to create an object of an inherited class and pass it to another function to do the push action. It is because ( in my case ) the queue is a part of a thread-safe queue and it has separate Push() method.
template<typename T>
void threadSafeQueue<T>::Push(T newData) { /* TODO: size check before pushing */
    std::shared_ptr<T> data(std::make_shared<T>(std::move(newData)));
                                           /* construct the object before lock*/
    std::lock_guard<std::mutex> lk(mut);
    taskQueue.push(data);
    dataCond.notify_one();
}
Earlier I did not have multiple tasks to execute ( or push ) into the queue, therefore 
threadSafeQueue<TaskRPCB> workQ declaration worked fine for me. 
Creating a base Task class like above is not working because of object slicing
Can you suggest other ways to store objects in the queue ( so that I can still use the lock guarded Push() method )
Thanks !
update : is the correct way of using variant?
typedef std::variant<TaskECB, TaskRPCB> myType;
int main() {
    queue<unique_ptr<myType>> Q;
    function<void()>    func1   = F1;
    function<void(int)> func2   = F2;
    TaskECB task1(func1);
    TaskRPCB task2(func2,4);
    myType x = task1;
    Q.push(make_unique<myType>(x));
    x = task2;
    Q.push(make_unique<myType>(x));
    if((*Q.front()).index() == 0) {
        auto f1 = get<TaskECB>(*Q.front());
        f1();
        Q.pop();
    }
    if((*Q.front()).index() == 1) {
        auto f1 = get<TaskRPCB>(*Q.front());
        f1();
        Q.pop();
    }
}
update2:
using myVariantType = std::variant<TaskECB, TaskRPCB>;
struct VisitPackage {
    void operator()(TaskECB & task) { 
        task();
    }
    void operator()(TaskRPCB& task) {
        task();
    }
};
int main() {
    queue<myVariantType> Q;
    function<void()>    func1   = F1;
    function<void(int)> func2   = F2;
    TaskECB task1(func1);
    TaskRPCB task2(func2,4);
    Q.emplace(task1);
    Q.emplace(task2);
    std::visit(VisitPackage(), Q.front());
    Q.pop();
    std::visit(VisitPackage(), Q.front());
    Q.pop();
}
