Undefined reference to deleted copy constructor

I have a recursive mutex in one of my classes. I instantiate that object in another class. Due to mutex non copyable, I deleted the copy constructor and copy assignment operator. However, the linker complains about undefined reference to the deleted copy constructor (compilation is fine). If I create a copy constructor which copies members except the mutex, the linking problem goes away. However, I will not make copies of the object with the recursive mutex so the ideal solution is just to delete the copy constructor.

This code compiles but fails on linking at the point when the class that uses it gets defined :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Biscuit {

Biscuit(const Biscuit& rhs);

recursive_mutex s_mutex;
......

};

Biscuit::Biscuit(const Biscuit& rhs) = delete;

.....

class Robot{ //undefined reference error occurs here

  Biscuit robot;
};


This code compiles fine and links fine :

1
2
3
4
Biscuit::Biscuit(const Biscuit& rhs){
    deviceDescriptor=rhs.deviceDescriptor;
    ledState=rhs.ledState;
};


The following is the actual error:

 
/home/cmisip/NB1/CV3-april-obs/Robot.h:46: undefined reference to `Biscuit::Biscuit(Biscuit const&)' 


I cannot seem to reproduce the error in a small test program. My only guess is somehow robot class is using a copy constructor somewhere in the code. In main.cpp, I have :

 
Robot create(get_frame, tag_objects, m_tagDetector);


But I dont think that is a copy constructor call;


I would rather do the first solution but dont know why Robot class is throwing an error. Any ideas?

Thanks
Chris
Last edited on
> Due to mutex non copyable, I deleted the copy constructor
> and copy assignment operator.
no need to do anything, the compiler will take care of that for you.

> Biscuit::Biscuit(const Biscuit& rhs) = delete;
¿why is that outside the class definition?
gcc and clang complain.


> I cannot seem to reproduce the error in a small test program.
¿what about on a big program?
I don't like working blind.
I traced it down to the thread call in the following code which is invoking the copy_constructor of Biscuit.
In my project code, there does not seem to be any side effect to defining an empty copy constructor for Biscuit class. At least not that I can immediately see.

The proper solution is to find out how to eliminate the need to use the copy_constructor of Biscuit with the thread call? I tried std::ref on the r instance but that does not eliminate the copy constructor call. It is necessary to include r in the thread call as I am passing it an object's member function.

The code below is just a simplification of my project code. I posted it on :

 
http://www.cplusplus.com/forum/general/197880/ 


with a different question, that regarding the multiple calls to Biscuit's Destructor. The answer might be related.

Thanks,
Chris


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include <cstdlib>
#include <memory>
#include <thread>
#include <deque>
#include <iostream>
#include <vector>
#include <atomic>
#include <unistd.h>

#include <ctime>


#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;
 
  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
 
  ch = getchar();
 
  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);
 
  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }
 
  return 0;
}

class Biscuit {
public:
    int x;
    
    Biscuit(){};
    ~Biscuit(){
        std::cout << "Deleting biscuit " <<std::endl;  //WHY
    }
    
    Biscuit(const Biscuit& rhs) {
            std::cout << "COPYING BISCUIT" << std::endl;
        }
    
    
};

class Robot;

typedef int(Robot::*m_behavior)(int);
typedef int(Robot::*m_listener)(int&);

struct cancelled_error {};
class thread_item;

class Behavior {
public:
Behavior(Robot &o, m_behavior b_have, std::string i_id): r(o), main_b(b_have),id(i_id) {};

void run_listeners();

int exec(int flag);

m_behavior main_b;
std::string id;
std::vector<std::shared_ptr<thread_item> >thread_list;
Robot &r;
};

struct State {
    std::shared_ptr<Behavior> current_behavior;
    int current_param;
};


class Stack {
    public:
    Stack() {};
    void empty(){
         state.clear();
    };
    
    State pop_out(){  //always pop from front
        State last=state.front();
        state.pop_front();
        return last;
    };
    
    void push_b(std::shared_ptr<Behavior> i_behavior, int iparam) { 
        State temp;
             temp.current_behavior=i_behavior;
             temp.current_param=iparam;        
             state.push_back(temp);
    };
    
    
    void push_f(std::shared_ptr<Behavior> i_behavior, int iparam) { 
        State temp;
              temp.current_behavior=i_behavior;
              temp.current_param=iparam;        
	      state.push_front(temp);
    };
    
    std::deque<State> state;
};



class Robot {
public:
Biscuit bisc;
    static std::atomic<bool> behavior_status;
    Robot() {  
    std::shared_ptr<Behavior> move=std::make_shared<Behavior>(*this,&Robot::move_and_scan,"M");
    run_stack->push_b(move,20);
    };

    int keyboard_interrupt(int &x){
      std::cout << "Keyboard running "<< x <<std::endl;
      int c;
        for (;;) {
            if (x!=1)
                break;
            
            if (kbhit()) {  
               if (getchar() == 'c') {
                  std::cout << "Key c pressed, stopping move_and_scan" << std::endl;
                  behavior_status=false;
               }
            }   
        }
      std::cout << "Keyboard interrupt ending" << std::endl;
    }
    
    int speech_interrupt(int &x){
      std::cout << "Speech running "<< x <<  std::endl;
      int c;
        for (;;) {
            if (x!=1)
                break;
            
            }
            //if speech detected
                //status=false;
      std::cout << "Speech interrupt ending" << std::endl;
    }
    
    int move_and_scan(int x){
        bool status=true;
        std::cout << "Move starting " << std::endl;
        behavior_status=true;
        int start_time=clock();
        int elapsed_time=0;
        do {
            if (!behavior_status) {
                std::cout << "Moved was stopped " << std::endl;
                status=false;
                break;
            }
            elapsed_time=clock() - start_time;
        } while (elapsed_time < (x*1000000));
        std::cout << "Move stopping with result "<< status << std::endl;
    }
    
    void run_state() {
           if (run_stack->state.size() > 0) {
                std::cout << "Run stack size is  " << run_stack->state.size() << std::endl;
                s=run_stack->pop_out();
                int outcome=s.current_behavior->exec(s.current_param);
           }
    }
           std::shared_ptr<Stack> run_stack=std::make_shared<Stack>();
           State s;
};

std::atomic<bool> Robot::behavior_status{0};

class thread_item {
    public:
        thread_item(Robot &o, m_listener m_list, std::string i_id) : r(o), id(i_id) {
            thr=std::make_shared<std::thread>(m_list,r,std::ref(l_flag));
        }
        
        std::string id;
        int l_flag=1;
        std::shared_ptr<std::thread> thr;
        Robot &r;
    
};


void Behavior::run_listeners() {
    std::shared_ptr<thread_item>m=std::make_shared<thread_item>(std::ref(r),&Robot::keyboard_interrupt,"KL");
    thread_list.push_back(m);
    std::shared_ptr<thread_item>x=std::make_shared<thread_item>(std::ref(r),&Robot::speech_interrupt,"KL");
    thread_list.push_back(x);
   
}


int Behavior::exec(int flag){
       run_listeners();
       std::cout << "Executing behavior "  << this->id << std::endl;  
       
       bool result = (r.*(this->main_b))(flag);
       
       for ( auto &l: thread_list ) {
           l->l_flag=0;
       }
       
       for ( auto &l: thread_list ) {
         l->thr->join();
       }
} 

int main(int argc, char** argv) {
    Robot create;
    
    //for (;;) {
        create.run_state();
    //}
    
    return 0;
}

I don't see the mutex in your `Biscuit' class.

deleting the copy constructor and changing line 193 to thr = std::make_shared<std::thread>(m_list, &r, std::ref(l_flag)); compiles, but it crash later «Process terminating with default action of signal 4 (SIGILL)»

So I fixed the warnings about not returning a value from the function and now it runs fine (simply doing `./a.out', don't know what kind of input you want)
I forgot to put the mutex in but your change did fix it. The fix was changing r to &r. Now there is no call to the copy constructor. Before your fix, the copy constructor for Biscuit got called when a thread is started. This also answered my question in the other post as the output is now:

1
2
3
4
5
6
7
8
9
Run stack size is  1
Keyboard running 1
Speech running 1
Executing behavior M
Move starting 
Move stopping with result 1
Speech interrupt ending
Keyboard interrupt ending
Deleting biscuit  //Only one call to the Destructor in Biscuit 


I was witnessing the creation and destruction of copies of r that I didn't realize were being made with each thread call.

The following code is the final working version with the mutex and the change from r to &r for the thread_item constructor.

Thanks,
Chris

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#include <cstdlib>
#include <memory>
#include <thread>
#include <deque>
#include <iostream>
#include <vector>
#include <atomic>
#include <unistd.h>

#include <ctime>


#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <mutex>

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;
 
  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
 
  ch = getchar();
 
  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);
 
  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }
 
  return 0;
}

class Biscuit {
public:
    int x;
    std::recursive_mutex s_mutex;
    
    Biscuit(){};
    ~Biscuit(){
        std::cout << "Deleting biscuit " <<std::endl;  
    }
    
    Biscuit(const Biscuit& rhs) = delete;
    
    
   // Biscuit(const Biscuit& rhs) {
    //        std::cout << "COPYING BISCUIT" << std::endl;
   //     }
    
    
};

class Robot;

typedef int(Robot::*m_behavior)(int);
typedef int(Robot::*m_listener)(int&);

struct cancelled_error {};
class thread_item;

class Behavior {
public:
Behavior(Robot &o, m_behavior b_have, std::string i_id): r(o), main_b(b_have),id(i_id) {};

void run_listeners();

int exec(int flag);

m_behavior main_b;
std::string id;
std::vector<std::shared_ptr<thread_item> >thread_list;
Robot &r;
};

struct State {
    std::shared_ptr<Behavior> current_behavior;
    int current_param;
};


class Stack {
    public:
    Stack() {};
    void empty(){
         state.clear();
    };
    
    State pop_out(){  //always pop from front
        State last=state.front();
        state.pop_front();
        return last;
    };
    
    void push_b(std::shared_ptr<Behavior> i_behavior, int iparam) { 
        State temp;
             temp.current_behavior=i_behavior;
             temp.current_param=iparam;        
             state.push_back(temp);
    };
    
    
    void push_f(std::shared_ptr<Behavior> i_behavior, int iparam) { 
        State temp;
              temp.current_behavior=i_behavior;
              temp.current_param=iparam;        
	      state.push_front(temp);
    };
    
    std::deque<State> state;
};



class Robot {
public:
Biscuit bisc;
    static std::atomic<bool> behavior_status;
    Robot() {  
    std::shared_ptr<Behavior> move=std::make_shared<Behavior>(*this,&Robot::move_and_scan,"M");
    run_stack->push_b(move,20);
    };

    int keyboard_interrupt(int &x){
      std::cout << "Keyboard running "<< x <<std::endl;
      int c;
        for (;;) {
            if (x!=1)
                break;
            
            if (kbhit()) {  
               if (getchar() == 'c') {
                  std::cout << "Key c pressed, stopping move_and_scan" << std::endl;
                  behavior_status=false;
               }
            }   
        }
      std::cout << "Keyboard interrupt ending" << std::endl;
    }
    
    int speech_interrupt(int &x){
      std::cout << "Speech running "<< x <<  std::endl;
      int c;
        for (;;) {
            if (x!=1)
                break;
            
            }
            //if speech detected
                //status=false;
      std::cout << "Speech interrupt ending" << std::endl;
    }
    
    int move_and_scan(int x){
        bool status=true;
        std::cout << "Move starting " << std::endl;
        behavior_status=true;
        int start_time=clock();
        int elapsed_time=0;
        do {
            if (!behavior_status) {
                std::cout << "Moved was stopped " << std::endl;
                status=false;
                break;
            }
            elapsed_time=clock() - start_time;
        } while (elapsed_time < (x*1000000));
        std::cout << "Move stopping with result "<< status << std::endl;
    }
    
    void run_state() {
           if (run_stack->state.size() > 0) {
                std::cout << "Run stack size is  " << run_stack->state.size() << std::endl;
                s=run_stack->pop_out();
                int outcome=s.current_behavior->exec(s.current_param);
           }
    }
           std::shared_ptr<Stack> run_stack=std::make_shared<Stack>();
           State s;
};

std::atomic<bool> Robot::behavior_status{0};

class thread_item {
    public:
        thread_item(Robot &o, m_listener m_list, std::string i_id) : r(o), id(i_id) {
            thr=std::make_shared<std::thread>(m_list,&r,std::ref(l_flag));
        }
        
        std::string id;
        int l_flag=1;
        std::shared_ptr<std::thread> thr;
        Robot &r;
    
};


void Behavior::run_listeners() {
    std::shared_ptr<thread_item>m=std::make_shared<thread_item>(r,&Robot::keyboard_interrupt,"KL");
    thread_list.push_back(m);
    std::shared_ptr<thread_item>x=std::make_shared<thread_item>(r,&Robot::speech_interrupt,"KL");
    thread_list.push_back(x);
   
}


int Behavior::exec(int flag){
       run_listeners();
       std::cout << "Executing behavior "  << this->id << std::endl;  
       
       bool result = (r.*(this->main_b))(flag);
       
       for ( auto &l: thread_list ) {
           l->l_flag=0;
       }
       
       for ( auto &l: thread_list ) {
         l->thr->join();
       }
} 

int main(int argc, char** argv) {
    Robot create;
    
    //for (;;) {
        create.run_state();
    //}
    
    return 0;
}

    int keyboard_interrupt(int &x){
150:5: warning: no return statement in function returning non-void [-Wreturn-type]

    int speech_interrupt(int &x){
163:5: warning: no return statement in function returning non-void [-Wreturn-type]

    int move_and_scan(int x){
180:5: warning: no return statement in function returning non-void [-Wreturn-type]

int Behavior::exec(int flag){
231:1: warning: no return statement in function returning non-void [-Wreturn-type]
you ought to fix those.
Yes, thanks. I have it integrated into my project code with the proper return types.

Chris
Topic archived. No new replies allowed.