C++11 中是否有模板来推断将值传递给函数时要使用的最佳类型

Is there a template in C++11 to deduce the optimal type to use when passing a value to a function?

本文关键字:函数 类型 最佳 值传 是否 C++11      更新时间:2023-10-16

我想写一个模板函数

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

这样,如果v足够小,它将按值传递,否则通过对常量的引用传递。为此,我使用了一个小帮手

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 );

现在,在 C++11 中:这种帮助程序模板是否仍然有意义,或者是否有更好的方法来达到相同的效果?是否已经有现成的模板?我检查了<type_traits>但找不到任何似乎相关的内容。

使用 C++11,您可以定义别名模板并节省一些键入时间。

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

然后将其用作

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

AFAIK,标准库中没有内置任何内容。此外,您将失去实现这种特征的模板参数推导,在我看来,这会大大降低其效用。

我认为 C++11 在这方面没有任何新内容,但是......

我的建议是实际内化基本规则并直接使用它们。在某些情况下,即使类型小于 4 个字节,您也可能希望通过 const 引用传递(该函数将存储引用以供以后使用,尽管它不应该更改字段,但它需要访问更新的值(。

相反,如果函数无论如何都要创建副本,您可能希望按值传递,以便在界面中完成复制,并且可以省略副本或将其更改为移动操作,从而可能降低操作的成本。

我想编写一个模板函数

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

这样,如果v足够小,它将按值传递,否则通过对常量的引用传递。

编译器可以足够聪明,可以在没有任何模板魔术的情况下做正确的事情,特别是如果函数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未内联,则程序集会有所不同。