Search code examples
c++templatesconst-reference

automatically use const-ref by big parameters


When I have following pseudo-class:

template <class T> class tmplClass
{
    void doSomething(T input);
};

Is there a way to change void doSomething(T input) to void doSomething(const T& input) when sizeof(T) is larger the the system architecture.

Means, when you have tmplClass<char> c; for example, use void doSomething(T input) and when you have tmplClass<[another big class with lots of variables]> use void doSomething(const T& input)

  1. Do I get any optimization out of this?
  2. Is there anything I have to do, or can gcc do this automatically
  3. If I have to do something, what?

Solution

  • Sure:

    template<typename T, bool=true>
    struct eff_arg {
      typedef T type;
    };
    template<typename T>
    struct eff_arg<T, (sizeof(T)>sizeof(int))> {
      typedef T const& type;
    };
    // C++11 addition
    
    template<typename T>
    using EffArg = typename eff_arg<T>::type;
    

    use:

    template <class T> class tmplClass
    {
      // C++11
      void doSomething(EffArg<T> input);
      // C++03
      void doSomething(typename eff_arg<T>::type input);
    };
    

    and replace sizeof(int) with whatever type you want to use as the "point where you want to pass as a reference instead of by value".

    Note that the size of a parameter is a mediocre way to make this decision: An extremely small class (even smaller than a pointer!) could have deep copy semantics where a large structure is duplicated when it is duplicated. And often the cut-off shouldn't be size of int or a pointer but bigger than that, because indirection has a cost.

    An idea might be to copy only objects that don't manage resources and are sufficiently small. std::is_trivially_copyable<T>::value && (sizeof(T) <= 2*sizeof(void*)) might be the kind of check you should make before passing a T rather than a T const& when you have no need for a copy of the data.

    This leads to the following:

    template<typename T, bool=true>
    struct eff_arg {
      typedef T const& type;
    };
    template<typename T>
    struct eff_arg<T,
      std::is_trivially_copyable<T>::value
      && (sizeof(T)<=2*sizeof(void*))
    > {
      typedef T type;
    };
    template<typename T>
    using EffArg = typename eff_arg<T>::type;