简体   繁体   中英

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

I'd like to write a template function

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

such that v will be passed by value if it's small enough, otherwise by reference-to-const. For this, I used a little helper

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

in the past such that I can have

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

Now, in C++11: does this kind of helper template still make sense, or is there a better way to achieve the same effect? Is there maybe a ready-made template already? I checked <type_traits> but couldn't spot anything which seemed relevant.

With C++11 you can define an alias template and save yourself some typing.

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

and then use it as

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

AFAIK, there's nothing built into the standard library for this. Also, you will lose template argument deduction implementing such a trait, which, in my opinion, reduces its utility greatly.

I don't think there is anything new in C++11 to this regard, but...

My recommendation is to actually internalize the basic rules and use them directly. There are cases where even if the type is less than 4 bytes you might want to pass by const reference (the function is going to store the reference for later and although it should not change the field it needs to access the updated value).

On the opposite direction if the function is going to make a copy anyways you might want to pass by value so that the copy is done in the interface and the copy can be elided or changed into a move operation, potentially reducing the cost of the operation.

I'd like to write a template function

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

such that v will be passed by value if it's small enough, otherwise by reference-to-const.

The compiler can be smart enough to do the right thing without any template magic, especially if the function f can be inlined. I would always implement f as

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

and trust the compiler to turn that into a copy if the copy is cheaper.

Here is an example:

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

I ran clang++ -O3 -std=c++11 -S -emit-llvm filename.cpp and the generated assembly (as far as I can tell) is the same.

Pass by reference:

@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
}

Pass by value:

@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
}

If f is not inlined then the assembly is different though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM