Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?
like image 735
Uroc327 Avatar asked Dec 05 '22 11:12

Uroc327


2 Answers

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;
like image 111
Yakk - Adam Nevraumont Avatar answered Dec 10 '22 19:12

Yakk - Adam Nevraumont


Yes, easy:

#include <type_traits>

template <typename T> struct Foo
{
    void do_something(typename std::conditional<(sizeof(T) > sizeof(void *)),
                                                 T const &, T>::type x)
    {
        // ... use "x"
    }

    // ...
};

You might want to assign the resulting type to some type alias for easy reuse.

As @Yakk suggests, it might be good to also add std::is_trivially_copyable<T>::value to the condition to avoid accidentally copying something that's expensive to copy or that might throw.

like image 42
Kerrek SB Avatar answered Dec 10 '22 19:12

Kerrek SB