Reputation: 13
I was trying to derive std::unique_lock and I ran across an issue that I am not otherwise able to duplicate with a simpler class. Below is compilable code that replicates the problem:
#include <iostream>
#include <mutex>
#include <string>
template<class T>
class A : public std::unique_lock<T>
{
public:
template<typename... Args>
A(const std::string name, Args&&... args) : name_(name),
std::unique_lock<T>(args...) { }
virtual ~A() {} // if this destructor exists...
private:
std::string name_;
};
int main()
{
std::timed_mutex tm;
auto a = A<std::timed_mutex>("Hello", tm, std::defer_lock); // this line fails
A<std::timed_mutex> a("Hello", tm, std::defer_lock); // this line works
return 0;
}
If the virtual destructor exists ( which I need for my actual class ), then I cannot use the line with auto to instantiate the class because ultimately the ctor in std::unique_lock gets called is the deleted one that takes a const T& mutex which is deleted (b/c the lock class can't deal with const mutexes). I assume its calling that deleted const ctor because for some reason its calling the copy constructor in A() that takes as input a const &A (according to the error logs below). If I simply use the non auto style instantiation code (labelled), then it compiles fine.
I'm using gcc 5.4.1 with the set(CMAKE_CXX_STANDARD 11) setting. I've tried 14 and 17 as well, so I assume its not the cpp I'm using.
Upvotes: 0
Views: 340
Reputation: 30619
The compiler will not implicitly generate a move-constructor if your class contains a user-declared destructor, copy-constructor, or assignment operator. Since A
has no move-constructor, the compiler falls back to the copy-constructor, which is implicitly deleted because std::unique_lock
's copy-constructor is deleted.
You can explicitly declare a move-constructor to get things working:
template<class T>
class A : public std::unique_lock<T>
{
public:
template<typename... Args>
A(const std::string name, Args&&... args)
: std::unique_lock<T>(args...),
name_(name)
{ }
A(A&&) = default;
virtual ~A() {}
private:
std::string name_;
};
You should also probably declare a move-assignment operator, but that's not necessary in this case.
Upvotes: 1