2

I have the following class:

template<typename T>
class Foo {
   // ...
   void frobnicate(const T& arg) {
       // [lots of code here]
       do_the_thing(arg);
       // [lots of code here]
   }

   void frobnicate(T&& arg) {
       // [lots of code here, same as above]
       do_the_thing(std::move(arg));
       // [lots of code here, same as above]
   }
}

Is there a way to remove the code duplication between the two versions of frobnicate without introducing "post" and "pre" helper functions?

Ideally, I'd like to write something like:

   void frobnicate(/*what goes here?*/ arg) {
       // [lots of code here]
       if (is_rvalue_reference(arg))
          do_the_thing(std::move(arg));
       else
          do_the_thing(arg);
       // [lots of code here]
   }

Is there a way to do this?

I think if T wasn't a class template argument but a function template argument, I could probably use template argument deduction in some clever way (though I'm not sure how exactly I'd do that either), but with the class involved I am not sure where to start...

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Nikratio
  • 2,338
  • 2
  • 29
  • 43
  • Does [this](https://stackoverflow.com/questions/24497311/is-this-a-universal-reference-does-stdforward-make-sense-here/24497361#24497361) help? – Zuodian Hu Apr 05 '20 at 13:53

1 Answers1

1

I think the best way is to make the function template and take advantage of the forwarding reference, which could reserve the value category of the argument.

template<typename T>
class Foo {
   template <typename X>
   void frobnicate(X&& arg) {
       // [lots of code here]
       do_the_thing(std::forward<X>(arg));
       // [lots of code here]
   }
};

If you want to restrict the type accepted by frobnicate to be T, you can apply static_assert or std::enable_if for frobnicate, e.g.

template<typename T>
class Foo {
   template <typename X>
   void frobnicate(X&& arg) {
       static_assert(std::is_same_v<T, std::decay_t<X>>, "X must be the same type of T");
       // [lots of code here]
       do_the_thing(std::forward<X>(arg));
       // [lots of code here]
   }
};

or

template<typename T>
class Foo {
   template <typename X>
   std::enable_if_t<std::is_same_v<T, std::decay_t<X>>> frobnicate(X&& arg) {
       // [lots of code here]
       do_the_thing(std::forward<X>(arg));
       // [lots of code here]
   }
};
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Thanks! Do you have any advice on choosing between `static_assert` and `enable_if`? – Nikratio Apr 05 '20 at 14:08
  • @Nikratio I prefer to `static_assert` because you can customize the compile-error message. `std::enable_if` is used for [SFINAE](https://en.cppreference.com/w/cpp/language/sfinae) more. – songyuanyao Apr 05 '20 at 14:09
  • For an l-value reference, the type is deduced as `X&`, so the static_assert or the enable_if will fail even if it's in principle correct. Rather use a std::decay_t inside std::is_same. – davidhigh Apr 05 '20 at 14:14
  • I can't get it to work even with `decay_t` (the assertion triggers when passing both rvalue and lvalue)... – Nikratio Apr 05 '20 at 14:23
  • @Nikratio I tried a demo [here](https://wandbox.org/permlink/DGVKi631GqM7KqpK). Could you make one which describes your error too? – songyuanyao Apr 05 '20 at 14:28
  • Thanks for the example! This helped me track down the problem, it's working now. – Nikratio Apr 05 '20 at 15:47