This code raises a warning in clang tidy:
Class 'Locker' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operatorclang-tidy(cppcoreguidelines-special-member-functions)
This is the updated struct, according to the comments:
struct Locker
{
std::binary_semaphore *sem = nullptr;
// ----------------------------------
// Methods
// ----------------------------------
auto try_lock(std::binary_semaphore &sem_, u32 time_in_seconds = 1) -> bool;
auto manual_release() -> void;
// ----------------------------------
// Deleted
// ----------------------------------
Locker(Locker &) = delete;
Locker(Locker &&) = delete;
Locker(std::binary_semaphore &&sem_) noexcept = delete;
Locker(std::binary_semaphore &sem_) noexcept = delete;
Locker(std::binary_semaphore *sem_) noexcept = delete;
Locker() noexcept = default;
auto operator=(std::binary_semaphore &sem_) noexcept -> Locker & = delete;
auto operator=(std::binary_semaphore &&sem_) noexcept -> Locker & = delete;
auto operator=(std::binary_semaphore *sem_) noexcept -> Locker & = delete;
// ----------------------------------
// Destructor
// ----------------------------------
~Locker()
{
manual_release();
}
};
I don't want any constructors but I want a specific destructor.
we have method to try lock and the destructor just release the lock cleanly making sure everything is ok.
Please note that GCC 12.2 with
-Wall -Wextra -pedantic -pedantic-errors -Werror -Wuninitialized -Wtrivial-auto-var-init -Wshadow -Wnormalized -Wno-error=comment
doesn't even bother.
How to suppress that warning?
Thanks!
Edit:
Below is the godbolt link to replicate the situation, may someone correct my code and share the link here?
Link to clang-tidy struct issue
Mostly by help from Aconcagua and n.m. we found out what your misconception was.
This has the wrong signature:
Locker( Locker &) = delete;
Copy constructor takes a const Locker&. And you do not delete the assignment. Those are the five:
Locker(const Locker &) = delete;
Locker(Locker &&) = delete;
Locker& operator=(const Locker&) = delete;
Locker& operator=(Locker&&) = delete;
~Locker() {
std::cout << "";
}
If you explicitly declare them all, the warning should be gone: https://godbolt.org/z/eP8TrE9b1
Related
Consider the following code:
#include <variant>
#include <cassert>
struct foo {
foo() noexcept;
foo(const foo&) noexcept = default;
foo(foo&&) noexcept = default;
foo& operator=(const foo&) noexcept = default;
foo& operator=(foo&&) noexcept = default;
};
std::variant<std::monostate, foo> var;
foo::foo() noexcept {
assert(!var.valueless_by_exception());
};
int main() {
var.emplace<foo>();
}
With libstdc++ (from GCC 11), this works, but with libc++ (from LLVM 12), and MSVC, the assert fails.
Which standard library implements the correct behaviour ? At no point any exception is thrown, and my type is entirely noexcept, so I'd expect "valueless_from_exception" to never be true.
To quote the standard (https://timsong-cpp.github.io/cppwp/n4861/variant#status):
A variant might not hold a value if an exception is thrown during a type-changing assignment or emplacement.
here I am clearly not in that case.
The standard doesn't currently provide an answer to your question, but the direction that LWG appears to be moving in is that your code will have undefined behaviour.
Considering the following code:
// in main.cpp
#include <type_traits>
struct A {
A& operator=(const A&) = default;
A& operator=(A&&) = default;
};
int main() {
static_assert(std::is_pod<A>::value);
return 0;
}
You can see there's a default move assignment operator for struct A.
In msvc(VS2017), with cl /std:c++17 main.cpp, I got a static assertion failure.
In g++(MinGW-W64, 8.1.0), with g++ -std=c++17 main.cpp, nothing went wrong.
However, if I comment out A& operator=(A&&) = default;, everything is ok in both two compilers.
So, can a POD type have a default move assignment operator with explicitly declared?
Yes, POD type can have an explicitly declared default move assignment operator:
https://en.cppreference.com/w/cpp/named_req/PODType
It was a bug in MSVC manifested up to compiler version 19.26, and fixed in 19.27. Demo: https://gcc.godbolt.org/z/c1o5fds3x
// Task.h
class Task
{
public:
Task() = default;
Task(const Task &) = delete;
~Task() = default;
Task(Task &&) = default;
const Task & operator= (const Task &) = default;
};
/main.cpp
#include <iostream>
#include "Task.h"
int main()
{
Task t;
std::cout<<"hello world"<<std::endl;
return 0;
}
I'm coding c++ on Mac OS. When I compile the code above: g++ main.cpp, I get the error as below:
error: explicitly-defaulted copy assignment operator must return 'Task
&'
I don't understand at all. operator= can ONLY return non-const reference here? I executed the same code in Windows and it worked without any error. So Mac OS has some special c++ standard?
The problem is that I use = default.
http://en.cppreference.com/w/cpp/language/copy_assignment
If = default is used, the type of return must be non-const reference. Whereas if we code like this: const Task & operator= (const Task &t){}, it works without any error.
The following code fails with gcc 4.8.0 (mingw-w64) with -O2 -std=c++11 -frtti -fexceptions -mthreads
#include <string>
class Param
{
public:
Param() : data(new std::string) { }
Param(const std::string & other) : data(new std::string(other)) { }
Param(const Param & other) : data(new std::string(*other.data)) { }
Param & operator=(const Param & other) {
*data = *other.data; return *this;
}
~Param() {
delete data;
}
Param & operator=(Param &&) = delete;
private:
std::string * data;
};
int main()
{
Param param;
param = Param("hop");
return 0;
}
With the error : error: use of deleted function 'Param& Param::operator=(Param&&)'
On the line :
param = Param("hop");
And compiles well if I remove the move assignment delete line.
There should be no default move assignment operator since there are user defined copy constructors, user defined copy assignment, and destructors, so deleting it should not affect the compilation, why is it failing?
And why is the allocation simply not using a copy assignment?
The function you deleted is exactly the assignment operator you try to use in main. By explicitly defining it as deleted you declare it and at the same time say using it is an error. So when you try to assign from an rvalue (Param("hop")), the compiler first looks whether a move assignment operator was declared. Since it was and is the best match, it tries to use it, just to find that it was deleted. Thus the error.
Here's another example of this mechanism which uses no special functions:
class X
{
void f(int) {}
void f(short) = delete;
};
int main()
{
X x;
short s;
x.f(s); // error: f(short) is deleted.
}
Removing the deleted f(short) will cause the compiler to select the non-deleted f(int) and thus compile without error.
Clang 3.2 reports an error in the following code, and I do not understand why there is a problem. The error occurs only in the template function, and only if braces are used for initialization. The other two initializations work as expected.
struct foo {
foo() { }
~foo() = default;
// deleted
foo(const foo& rhs) = delete;
foo(foo&& rhs) noexcept = delete;
auto operator=(const foo& rhs) -> foo& = delete;
auto operator=(foo&& rhs) noexcept -> foo& = delete;
};
template <typename Type>
void bar() {
foo a; // OK
foo b{}; // ERROR
}
int main() {
foo c{}; // OK
bar<int>();
}
If I compile the code with clang++ -Wall -std=c++11 -c, Clang prints the following error message:
bug.cpp:14:9: error: conversion function from 'foo' to 'foo' invokes a deleted function
foo b{}; // ERROR
^
bug.cpp:19:5: note: in instantiation of function template specialization 'bar<int>' requested
here
bar<int>();
^
bug.cpp:6:5: note: function has been explicitly marked deleted here
foo(foo&& rhs) noexcept = delete;
^
1 error generated.
I have no idea why Clang tries to do a conversion. It sounds like a bug. Unfortunately, I have the problem in a more complex code base, where the solution is not as easy as just removing the braces.
Why does Clang need a conversion in this case? And how can I get it working in general?
This is definitely a bug. There is no reason why an attempt should be made to invoke a move constructor, since you have a default initialization:
foo b{}; // Same as "foo b;" in any case
If copy initialization were involved, things would be different, but that is not the case.
Besides, your code compiles fine on GCC 4.7.2.