Есть ли шаблон в С++ 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
не вставлен, сборка отличается, однако.