C++ Templates

【C++ Templates(7)】传值还是传引用

2018-04-19  本文已影响154人  downdemo

传值

template<typename T>
void printV (T arg) {
    ...
}

std::string s = "hi";
printV(s);
std::string returnString();
std::string s = "hi";
printV(s); // copy constructor
printV(std::string("hi")); // copying usually optimized away(if not, move constructor)
printV(returnString()); // copying usually optimized away(if not, move constructor)
printV(std::move(s)); // move constructor
template<typename T>
void printV (T arg) {
    ...
}

std::string const c = "hi";
printV(c); // c decays so that arg has type std::string
printV("hi"); // decays to pointer so that arg has type char const*
int arr[4];
printV(arr); // decays to pointer so that arg has type char const*
void printV (char const* arg)
{
    ...
}

传引用

传const引用

template<typename T>
void printR (T const& arg) {
    ...
}
std::string returnString();
std::string s = "hi";
printR(s); // no copy
printR(std::string("hi")); // no copy
printR(returnString()); // no copy
printR(std::move(s)); // no copy
int i = 42;
printR(i); // 传引用而不是拷贝i
// 模板实例化为
void printR(int const& arg) {
    ...
}
template<typename T>
void printR (T const& arg) {
    ...
}

std::string const c = "hi";
printR(c); // T deduced as std::string, arg is std::string const&
printR("hi"); // T deduced as char[3], arg is char const(&)[3]
int arr[4];
printR(arr); // T deduced as int[4], arg is int const(&)[4]

传non-const引用

template<typename T>
void outR (T& arg) {
    ...
}

// 临时变量(prvalue)或std::move()传递的对象(xvalue)是不允许的
std::string returnString();
std::string s = "hi";
outR(s); //OK: T deduced as std::string, arg is std::string&
outR(std::string("hi")); //ERROR: not allowed to pass a temporary (prvalue)
outR(returnString()); // ERROR: not allowed to pass a temporary (prvalue)
outR(std::move(s)); // ERROR: not allowed to pass an xvalue

// 可以传递non-const原始数组
int arr[4];
outR(arr); // OK: T deduced as int[4], arg is int(&)[4]
// 可以修改元素,处理数组大小
template<typename T>
void outR (T& arg) {
    if (std::is_array<T>::value) {
        std::cout << "got array of " << std::extent<T>::value << " elems\n";
    }
    ...
}
std::string const c = "hi";
outR(c); // OK: T deduced as std::string const
outR(returnConstString()); // OK: same if returnConstString() returns const string
outR(std::move(c)); // OK: T deduced as std::string const
outR("hi"); // OK: T deduced as char const[3]
template<typename T>
void outR (T& arg) {
static_assert(!std::is_const<T>::value, "out parameter of foo<T>(T&) is const");
    ...
}
template<typename T,
typename = std::enable_if_t<!std::is_const<T>::value>
void outR (T& arg) {
    ...
}
template<typename T>
requires !std::is_const_v<T>
void outR (T& arg) {
    ...
}

传转发引用

*使用引用调用的一个原因是为了完美转发参数,但记住使用转发引用(定义为一个模板参数的右值引用)时,适用特殊的类型推断规则

template<typename T>
void passR (T&& arg) { // arg declared as forwarding reference
    ...
}

// 可以传所有东西给转发引用,不会创建拷贝
std::string s = "hi";
passR(s); // OK: T deduced as std::string& (also the type of arg)
passR(std::string("hi")); // OK: T deduced as std::string, arg is std::string&&
passR(returnString()); // OK: T deduced as std::string, arg is std::string&&
passR(std::move(s)); // OK: T deduced as std::string, arg is std::string&&
passR(arr); // OK: T deduced as int(&)[4] (also the type of arg)
std::string const c = "hi";
passR(c); // OK: T deduced as std::string const&
passR("hi"); // OK: T deduced as char const(&)[3] (also the type of arg)
int arr[4];
passR(arr); // OK: T deduced as int (&)[4] (also the type of arg)
template<typename T>
void passR(T&& arg) { // arg是一个传递的左值的转发引用
    T x; // x是一个需要初始化的引用
    ...
}
foo(42); // OK: T推断为int
int i;
foo(i); // ERROR: T推断为int&,使得模板中的x声明无效

使用std::ref()和std::cref()

template<typename T>
void printT (T arg) {
    ...
}

std::string s = "hello";
printT(s); // pass s by value
printT(std::cref(s)); // pass s “as if by reference”
#include <functional>  // for std::cref()
#include <string>
#include <iostream>

void printString(std::string const& s)
{
    std::cout << s << '\n';
}

template<typename T>
void printT (T arg)
{
    printString(arg);        // might convert arg back to std::string
}

int main()
{
    std::string s = "hello";
    printT(s);               // print s passed by value
    printT(std::cref(s));    // print s passed ''as if by reference''
}
template<typename T>
void printV (T arg) {
    std::cout << arg << '\n';
}
...
std::string s = "hello";
printV(s); // OK
printV(std::cref(s)); // ERROR: no operator << for reference wrapper defined
template<typename T1, typename T2>
bool isless(T1 arg1, T2 arg2)
{
    return arg1 < arg2;
}
...
std::string s = "hello";
if (isless(std::cref(s) < "world")) ... // ERROR
if (isless(std::cref(s) < std::string("world"))) ... // ERROR

处理字符串字面值和原始数组

template<typename T>
void foo (T const& arg1, T const& arg2)
{
    ...
}
foo("hi", "guy"); // ERROR:char const[3] and char const[4]
template<typename T>
void foo (T arg1, T arg2)
{
    ...
}
foo("hi", "guy"); //compiles, but ...
template<typename T>
void foo (T arg1, T arg2)
{
    if (arg1 == arg2) { // OOPS: compares addresses of passed arrays
        ...
    }
}
foo("hi", "guy"); // compiles, but ...

字符串字面值和原始数组的特殊实现

template<typename T, std::size_t L1, std::size_t L2>
void foo(T (&arg1)[L1], T (&arg2)[L2])
{
    T* pa = arg1; // decay arg1
    T* pb = arg2; // decay arg2
    if (compareArrays(pa, L1, pb, L2)) {
        ...
    }
}
template<typename T,
    typename = std::enable_if_t<std::is_array_v<T>>>
void foo (T&& arg1, T&& arg2)
{
    ...
}

处理返回值

std::string* s = new std::string("whatever");
auto& c = (*s)[0];
delete s;
std::cout << c; // run-time ERROR
auto s = std::make_shared<std::string>("whatever");
auto& c = (*s)[0];
s.reset();
std::cout << c; //run-time ERROR
template<typename T>
T retR(T&& p) // p is a forwarding reference
{
    return T{...}; // OOPS: returns by reference when called for lvalues
}
template<typename T>
T retV(T p) //Note: T might become a reference
{
    return T{...}; // OOPS: returns a reference if T is a reference
}
int x;
retV<int&>(x); // retT() instantiated for T as int&
template<typename T>
typename std::remove_reference<T>::type retV(T p)
{
    return T{...}; // always returns by value
}
template<typename T>
auto retV(T p) // by-value return type deduced by compiler
{
    return T{...}; // always returns by value
}

推荐的模板参数声明

不要过度泛型

template<typename T>
void printVector (std::vector<T> const& v)
{
    ...
}

std::make_pair()的例子

template<typename T1, typename T2>
pair<T1,T2> make_pair (T1 const& a, T2 const& b)
{
    return pair<T1,T2>(a,b);
}
template<typename T1, typename T2>
pair<T1,T2> make_pair (T1 a, T2 b)
{
    return pair<T1,T2>(a,b);
}
template<typename T1, typename T2>
constexpr pair<typename decay<T1>::type, typename
decay<T2>::type>
make_pair (T1&& a, T2&& b)
{
    return pair<typename decay<T1>::type,
        typename decay<T2>::type>(forward<T1>(a), forward<T2>(b));
}
上一篇 下一篇

猜你喜欢

热点阅读