4

Say I have two templates for an operator= overloading :

class MyClass {
public:
    template <typename T>
    std::enable_if_t<true == /* a condition */, MyClass&>
    operator=(T value) {
        std::cout << "Pass by copy" << std::endl;
    }

    template <typename T>
    std::enable_if_t<false == /* a condition */, MyClass&>
    operator=(const T &value) {
        std::cout << "Pass by reference" << std::endl;
    }
};

What would be the most optimal condition to use with std::enable_if ?

I came up with this :

template <typename T>
struct pass_copy_cond : std::conditional<
        std::is_fundamental<T>::value ||
        std::is_pointer<T>::value ||
        (std::is_trivial<T>::value && sizeof(T) < sizeof(void*))
    , std::true_type, std::false_type>::type {};

Is there a way to improve the condition ?

Fourmet
  • 538
  • 2
  • 12
  • 1
    If the implementation for pass-by-value and pass-by-reference would be the same, you can just use a `std::conditional_t` to determine the argument type instead of using `enable_if`. The advantage is you only have to write the function once, avoiding repeating yourself. – François Andrieux Apr 10 '19 at 17:00
  • Didn't think of it. However when I try it, the compiler becomes unable to deduce the `argument T`... – Fourmet Apr 10 '19 at 17:19
  • That's a good point, you won't be able to perform template deduction directly, and you can't explicitly give template arguments for an operator. You could have `operator()` forward perfectly to a private function that actually implements the behavior, but at that point I'm not sure it's still worth it. – François Andrieux Apr 10 '19 at 17:22

1 Answers1

1

Some checks are redundant:

All fundamental types and pointer types are also trivial. Admittedly, some fundamental types could be bigger than void* (pointers to member / member function, though they are likely larger, are not detected by std::is_pointer).

This is over-constraining:

Being trivially copyable is enough, trivial default-constructibility is irrelevant. One might even make a case for trivial destructibility alone to be sufficient.

template <class T>
using prefer_copy = std::bool_constant<std::is_trivially_copyable<T>() && sizeof(T) <= sizeof(std::max_align_t)>;

You will be writing the implementation twice

If your compiler allows you to force inlining (not standardised, but nearly every compiler allows it somehow), you can delegate to that singular common implementation and get it inlined.

Fourmet
  • 538
  • 2
  • 12
Deduplicator
  • 44,692
  • 7
  • 66
  • 118