3

When writing an overloaded function from an rvalue reference and a const reference, you might have code duplication, so I sometimes do both with the same code. As shown here:

#include <iostream>
#include <type_traits>

struct A {
  template <typename T>
  A& operator=(T&&) {
    if constexpr (::std::is_rvalue_reference_v<T&&>) {
      ::std::cerr << "operator= move\n";
    } else {
      ::std::cerr << "operator= copy\n";
    }
    return *this;
  };
};

Now it is my understanding that this should implement both an A& operator=(T const&) and an A& operator=(T&&). So given this code, in theory I would expect this invocation:

int main() {
  A a,b;
  a = b;
  a = ::std::move(b);
}

to produce the following output:

operator= copy
operator= move

However, to my surprise, the second line (!) is missing. How can I cover both at the same time?


I am compiling with g++ 8.3.0 and -std=c++17.

bitmask
  • 32,434
  • 14
  • 99
  • 159
  • See this [similar question](https://stackoverflow.com/questions/24832143/template-copy-constructor-does-not-prevent-compiler-generated-move-constructor). – 1201ProgramAlarm Apr 25 '19 at 00:09
  • @1201ProgramAlarm Oh, that's just bloody evil! Why in hell would they do this?! Anyway, thanks. – bitmask Apr 25 '19 at 00:14
  • 1
    @bitmask I suspect because copy/move constructors and assignment operators have specific properties, and making some template instantiations "special" is a can of worms that nobody wants to open. – molbdnilo Apr 25 '19 at 00:35

1 Answers1

0

Given the explanation pointed out by 1201ProgramAlarm, the interesting question is why it works for either situation. The answer is simple: you assigned a non-const object, so T was deduced as A& (and T&& also becomes A&), producing a strictly better match than the implicit copy constructor that accepts const A&. For the move constructor, or if you assigned a const A, the signatures are identical, and non-templates are preferred in that case.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76