unresolved external Symbol (Constructor)

10,812

There are many mistakes in your code, as I suggested you should learn more C++ basics before this.
As I showed the mistakes in my comments, let me answer your problem with the member function only:

C++ treats functions as first class citizens, as opposed to Java (Java 8 improvements fix some issues, but it does not treat functions as first class citizens neither).
C++ understands functions as a concept of callable entity: A callable entity is anything that could be called, i.e. treated as a function. So a callable entity could be:

  • A global function: Just the good old C function. It could be defined, implemented, and called:

    void f() {}
    
    int main()
    {
        f(); //Call to f
    }
    
  • A member function: The classic OO member function. Its called within an object, and operates on its data:

    struct foo
    {
        void f();   
    };
    
    int main()
    {
        foo myfoo;
    
        myfoo.f(); //Call to foo::f
    }
    
  • A static member function: Its a member function not linked to an object, it operates at class level, so its signature is the same as the signature of a global function (Remember this, its important as we will see later)

    struct foo
    {
        static void f();
    {
    
    int main()
    {
        foo::f(); //Call to foo::f
    }
    
  • A functor: A functor is just a class which objects are designed to work as they where functions. Thats achieved overloading the () operator:

    struct f
    {
        void operator()() const
        {}
    };
    
    int main()
    {
        f myf;
    
        myf(); //Call to foo
    }
    

    The standard library defines the template std::function, which is a type erased functor designed to hold any kind of callable entity:

    #include <functional>
    
    void f() {}
    
    int main()
    {
        std::function<void()> f_wrapper;
    
        f_wrapper(); //Call to f_wrapper, which is an indirect call to f
    } 
    
  • A lambda expression: A lambda expression is just an inplace-defined anonymous functor:

    int main()
    {
        std::function<void()> lambda = [](){ std::cout << "hello!"; };
    }
    

    Hello!

  • A pointer to a function: C allowed the user to store functions via function pointers, just like it allows store pointers to data. C++ has the same capabilities, extended for member functions too:

    void f() {}
    void g( void(*function)() ) 
    { 
        function(); //Call to the function referenced by the pointer passed as parameter 
    }
    
    int main()
    {
        g(f); //Call to g passing f as parameter. Its an indirect call to f. Note that the & is not needed
    }
    

    As we said above, static member functions have the same signature as global functions, so the syntax is exactly the same as in the example above.
    But for member functions is not the same: A member function is linked to an object, so its called within an object. The syntax of a member function pointer is as follows:

    struct foo
    {
        voif f();
    };
    
    typedef void(foo::* pointer_to_f_type)();
    
    int main()
    {
        pointer_to_f_pointer ptr = &foo::f; //Note that the & is needed, just like in variable pointers
        foo myfoo;
    
        (myfoo.*ptr)(); //Call to the foo member function pointed by ptr (foo::f) using myfoo as object
    }
    

    A member function pointer of an specific signature has nothing to do with a pointer to a global/static function of the same signature, and cannot be convertible from/to member-pointer to non member pointer and vice-versa.

    Because function pointers and member function pointers are completely separated things, we cannot treat any kind of function in an homogeneous way directly. For example, we cannot make an array which holds both function pointers and member function pointers. However, the standard library provides the function template std::bind, which allows us to bind a function to some (or all) call parameters. That is, the object returned by std::bind() represents a partial (or complete) call to a callable entity.
    For example:

     void f( int , int , int ) {}
    
     int main()
     {
         std::function<void(int,int,int)> f_wrapper = f;
    
         f(1,2,3); //Ok
         f_wrapper(1,2,3); //Ok
    
         std::function<void()> f_call = std::bind( f , 1 , 2 , 3 ); //f_call represents a partial call (Complete in this case) fo f
         f_call(); //Execute the call
    
         std::function<void(int)> partial_f_call = std::bind( f , std::placeholders::_1 , 2 , 3 );
         partial_f_call( 1 ); //Same execution as above
     } 
    

    As you can see, std::bind() allows us to bind certain parameters to a function, making a callable entity which represents a call to the function. So it could be used to bind an object to a member function, making a callable entity with exactly the same form of a std::function instance initialized with any other kind of callable entity. That is we could use std::function to store member and non-member functions in the same way, and use it in the same way**:

     void f();
    
     struct foo
     {
         void f();
     };
    
     int main()
     {
         std::vector<std::function<void()>> functions;
         foo myfoo;
    
         functions.push_back( f );
         functions.push_back( std::bind( &foo::f , myfoo ) );
         functions.push_back( [](){} );
         ...
    
         for( const auto& function : functions )
             function();
     }
    

As you can see, there are many forms of callable entities. One important point is that someone could use C++ templates and rely on duck typing to use a callable entity passed as parameter:

 template<typename F>
 void call_function( const F& function )
 {
     function(); //function should be any kind of thing which could be called, that is, a callable entity
 } 

That's exactly what the std::thread constructor does. It just takes any kind of callable entity, a set of parameters for the call, starts a new thread, and later the callable entity is called on that new thread (Via join() or detach(). Its implementation could be something like:

template<typename F , typename... ARGS>
thread::thread( F&& function , ARGS&&... args )
{
    _thread = create_thread();

    _function = std::bind( std::forward<F>( function ) , std::forward<ARGS>( args )... ); 
}


void thread::detach()
{
    detach_thread( _thread );
    _function();
}

void thread::join()
{
    join_thread( _thread );
    _function();
}

Of course this is not a working implementation, is just an overview :p

So now you can understand why your approach doesn't work, and what you could do to solve that. Specifically, use std::bind() to create a callable entity if you want to use a member function on the thread.

Share:
10,812
nidomiro
Author by

nidomiro

Updated on July 10, 2022

Comments

  • nidomiro
    nidomiro almost 2 years

    At building I get the following error:

    main.obj : error LNK2019: unresolved external symbol ""public: __cdecl Worker::Worker(void)" (??0Worker@@QEAA@XZ)" in function "main".

    main.obj : error LNK2019: unresolved external symbol ""public: virtual __cdecl Worker::~Worker(void)" (??1Worker@@UEAA@XZ)" in function "main".

    I cannot find the Problem. (I also looked here)

    main.cpp

    #include <iostream>
    #include <thread>
    #include "worker.h"
    
    using namespace std;
    
    void pause_thread(int n)
    {
        std::this_thread::sleep_for (std::chrono::seconds(n));
        std::cout << "pause of " << n << " seconds ended\n";
    }
    
    int main()
    {
        std::cout << "Spawning and detaching 3 threads...\n";
        std::thread (pause_thread,1).detach();
        std::thread (pause_thread,2).detach();
        std::thread (pause_thread,3).detach();
        std::cout << "Done spawning threads.\n";
    
        std::cout << "(the main thread will now pause for 5 seconds)\n";
        // give the detached threads time to finish (but not guaranteed!):
        pause_thread(5);
    
        Worker w;
    
    
    
        return 0;
    }
    

    worker.h

    #ifndef WORKER_H
    #define WORKER_H
    
    
    #include "jobqueue.h"
    #include "job.h"
    #include <mutex>
    #include <thread>
    
    using namespace std;
    
    class Worker
    {
    private:
        JobQueue jobs;
        mutex mu;
        thread & workerThread;
        bool stop;
    
        void work();
    
    public:
        Worker();
        virtual ~Worker();
        void addJob(Job*);
        int getJobCount();
    
    
    };
    
    #endif // WORKER_H
    

    worker.cpp

    #include "worker.h"
    
    Worker::Worker(): workerThread(work), stop(false)
    {
    }
    
    Worker::~Worker()
    {
        workerThread.join();
    }
    
    void Worker::work(){
        while (!stop) {
            unique_lock<mutex> lock(mu, defer_lock);
            lock.lock();
                Job* job = jobs.getNextJob();
            lock.unlock();
    
            job->run();
            delete job;
        }
    }
    
    void Worker::addJob(Job* job){
        jobs.append(job);
    }
    
    int Worker::getJobCount(){
        unique_lock<mutex> lock(mu);
        return jobs.size();
    }
    

    project.pro

    TEMPLATE = app
    CONFIG += console
    CONFIG -= app_bundle
    CONFIG -= qt
    
    SOURCES += main.cpp \
        jobqueue.cpp \
        worker.cpp
    
    HEADERS += \
        jobqueue.h \
        worker.h \
        job.h
    

    Deleting the Project.pro.user solves the (main-) problem, now errors are shown again

    • chris
      chris over 10 years
      Are you linking the file? stackoverflow.com/questions/12573816/…
    • ALiX
      ALiX over 10 years
      you have to provide all the object files for linking. You might have missed including the worker.o object file.
    • nidomiro
      nidomiro over 10 years
      As IDE I use QT Creator with MSVC 2012 64Bit as compiler. I'll post my projectfile above.
    • user1703401
      user1703401 over 10 years
      The calling convention for the constructor and deconstructor of a C++ class is __thiscall, never __cdecl. Very hard to guess how you did that.
    • Manu343726
      Manu343726 over 10 years
      workedThread is a reference to a std::thread. That makes no sense in the form you are using it. Its a typo?
    • Manu343726
      Manu343726 over 10 years
      What I don't understand is how your code even compiles: You are passing a member function to the thread initializer as if it was a normal function pointer, not a member function pointer. How do you expect this even woork? How the thread would call that?
    • Manu343726
      Manu343726 over 10 years
      Finally -1. Learn C++ before doing more complicated things
    • nidomiro
      nidomiro over 10 years
      After deleting the project.pro.user file I get the compile errors. @Manu343726 I'm lerning C++ with this. I switched from Java so I'm no pro in C++. In C++ you have to know all the errors in Java the compiler has better error detection. By the way what is the best apporach if I want to let the thread call the Worker::work() Method?
  • nidomiro
    nidomiro over 10 years
    Thanks for your work! This topic is still a bit cryptic for me. I solved my problems after the compiler gave me actual errors. My solution is still more Javastyle than C++style but its working for now. I'll refactor my code with your suggestions, thanks.
  • Manu343726
    Manu343726 over 10 years
    @niccomatik I know there is too many information on this post for a C++ newbbie. I was only trying to show you the reasons about your mistakes. I suggest you to read a good C++ book (checkout the C++FAQ of this site) and search along SO for good questions/answers about the most important topics of the language