I have a class in which I want to enable the copy/move assignment operators only if a type parameter to the class is nothrow copy/move constructible respectively. So I tries this:
#include <type_traits>
template<typename T>
struct Foobar {
Foobar(T value) : x(value) {}
Foobar(const Foobar &other) : x(other.x) {}
Foobar(Foobar &&other) : x(std::move(other.x)) {}
template<bool Condition = std::is_nothrow_copy_constructible<T>::value,
typename = typename std::enable_if<Condition>::type>
Foobar &operator=(const Foobar &rhs) {
x = rhs.x;
return *this;
}
template<bool Condition = std::is_nothrow_move_constructible<T>::value,
typename = typename std::enable_if<Condition>::type>
Foobar &operator=(Foobar &&rhs) {
x = std::move(rhs.x);
return *this;
}
T x;
};
int main() {
Foobar<int> foo(10);
Foobar<int> bar(20);
foo = bar;
foo.operator=(bar);
return 0;
}
Now, Clang gives me the following error:
enable_if_test.cpp:31:9: error: object of type 'Foobar<int>' cannot be assigned because its copy assignment operator is implicitly
deleted
foo = bar;
^
enable_if_test.cpp:8:5: note: copy assignment operator is implicitly deleted because 'Foobar<int>' has a user-declared move
constructor
Foobar(Foobar &&other) : x(std::move(other.x)) {}
^
enable_if_test.cpp:32:9: error: call to deleted member function 'operator='
foo.operator=(bar);
~~~~^~~~~~~~~
enable_if_test.cpp:4:8: note: candidate function (the implicit copy assignment operator) has been implicitly deleted
struct Foobar {
^
enable_if_test.cpp:12:13: note: candidate function [with Condition = true, $1 = void]
Foobar &operator=(const Foobar &rhs) {
^
enable_if_test.cpp:19:13: note: candidate function [with Condition = true, $1 = void] not viable: no known conversion from
'Foobar<int>' to 'Foobar<int> &&' for 1st argument
Foobar &operator=(Foobar &&rhs) {
^
2 errors generated.
Now, I included the explicit call to the assignment operator to showcase the weirdness of the error. It just says that my template is a candidate function and gives no reason for why it is not chosen.
My guess here is that since I defined a move constructor, the compiler implicitly deleted the copy assignment operator. Since it is implicitly deleted, it doesn't even want to instantiate any template. Why it behaves like this I do not know, however.
How can I achieve this behavior?
(Note: The actual code has legitimate reasons to define each of the members, I'm aware that it is useless here.)
operator=(Foo const&)
and a function template specializationoperator=<true, void>(Foo const&)
. And as a last resort, non-template functions are preferred over function template specializations - this selects the deleted operator. – Bilinear