Есть ли шаблон в С++ 11 для вывода оптимального типа для использования при передаче значения функции?

Я хотел бы написать функцию шаблона

template <typename T>
void f( T v );

так что v будет передаваться по значению, если оно достаточно мало, иначе ссылка-на-const. Для этого я использовал небольшой помощник

template <typename T, bool>
struct parameter_helper;

template <typename T>
struct parameter_helper<T, true> {
    typedef T type;
};

template <typename T>
struct parameter_helper<T, false> {
    typedef const T& type;
};

template <typename T>
struct parameter {
    typedef typename parameter_helper<T, sizeof(T) <= sizeof(void*)>::type type;
};

в прошлом, так что я мог бы иметь

template <typename T>
void f( typename parameter<T>::type v );

Теперь, в С++ 11: этот тип вспомогательного шаблона все еще имеет смысл или есть лучший способ добиться такого же эффекта? Возможно ли уже готовый шаблон? Я проверил <type_traits>, но не смог обнаружить ничего, что казалось актуальным.

Ответы

Ответ 1

С С++ 11 вы можете определить шаблон псевдонима и сохранить себе некоторую типизацию.

template<typename T> 
using parameter_t = typename parameter<T>::type;

а затем используйте его как

template <typename T>
void f( parameter_t<T> v ); 

AFAIK, для этого нет встроенной стандартной библиотеки. Кроме того, вы потеряете вывод аргумента шаблона, реализующий такую ​​черту, которая, на мой взгляд, значительно снижает ее полезность.

Ответ 2

Я не думаю, что в С++ 11 есть что-то новое, но...

Моя рекомендация - фактически интернализировать основные правила и использовать их напрямую. Бывают случаи, когда даже если тип меньше 4 байтов, вы можете пройти по ссылке const (функция будет хранить ссылку позже, и хотя она не должна изменять поле, ей необходимо получить доступ к обновленному значению).

В противоположном направлении, если функция собирается сделать копию в любом случае, вы можете пройти по значению, чтобы копия была выполнена в интерфейсе, и копия могла быть удалена или изменена в операцию перемещения, что потенциально уменьшило стоимость операции.

Ответ 3

Я хотел бы написать функцию шаблона

template <typename T>
void f( T v );

так что v будет передаваться по значению, если оно достаточно мало, иначе ссылка-на-const.

Компилятор может быть достаточно умным, чтобы делать правильные вещи без магии шаблонов,, особенно если функция f может быть встроена. Я всегда использовал бы f как

template <typename T> 
void f(const T& v);

и доверять компилятору, чтобы превратить это в копию, если копия дешевле.

Вот пример:

extern volatile int k;
extern volatile int m;

static void f(const int& j) noexcept { // or f(const int j)
   for (int i=0; i<j; ++i) {
    m = i;
  }
}

void g() noexcept {
  int j = k;
  f(j);
}

Я запустил clang++ -O3 -std=c++11 -S -emit-llvm filename.cpp, и сгенерированная сборка (насколько я могу судить) одинакова.

Перейдите по ссылке:

@k = external global i32
@m = external global i32

; Function Attrs: nounwind uwtable
define void @_Z1gv() #0 {
entry:
  %0 = load volatile i32* @k, align 4, !tbaa !0
  %cmp3.i = icmp sgt i32 %0, 0
  br i1 %cmp3.i, label %for.body.i, label %_ZL1fRKi.exit

for.body.i:                                       ; preds = %entry, %for.body.i
  %i.04.i = phi i32 [ %inc.i, %for.body.i ], [ 0, %entry ]
  store volatile i32 %i.04.i, i32* @m, align 4, !tbaa !0
  %inc.i = add nsw i32 %i.04.i, 1
  %exitcond = icmp eq i32 %inc.i, %0
  br i1 %exitcond, label %_ZL1fRKi.exit, label %for.body.i

_ZL1fRKi.exit:                                    ; preds = %for.body.i, %entry
  ret void
}

Перейдите по значению:

@k = external global i32
@m = external global i32

; Function Attrs: nounwind uwtable
define void @_Z1gv() #0 {
entry:
  %0 = load volatile i32* @k, align 4, !tbaa !0
  %cmp3.i = icmp sgt i32 %0, 0
  br i1 %cmp3.i, label %for.body.i, label %_ZL1fi.exit

for.body.i:                                       ; preds = %entry, %for.body.i
  %i.04.i = phi i32 [ %inc.i, %for.body.i ], [ 0, %entry ]
  store volatile i32 %i.04.i, i32* @m, align 4, !tbaa !0
  %inc.i = add nsw i32 %i.04.i, 1
  %exitcond.i = icmp eq i32 %inc.i, %0
  br i1 %exitcond.i, label %_ZL1fi.exit, label %for.body.i

_ZL1fi.exit:                                      ; preds = %for.body.i, %entry
  ret void
}

Если f не вставлен, сборка отличается, однако.