如果模板模板参数是矢量,则需要不同的行为

时间:2018-07-15 15:41:34

标签: c++ templates stl c++14 variadic-templates

我们正在使用一个内部std::vector作为输入的内部c ++库,但是我想编写应该能够接受std::vectorstd::setstd::unordered_set的包装函数当传递给该包装函数的输入是std::vector本身时,我不想将其复制到临时向量中,因此有什么方法可以避免这种不必要的复制。

示例参考代码将更清楚地说明此问题:

#include <iostream>
#include <set>
#include <vector>
#include <unordered_set>
void print(const std::vector<int>& argVec)
{
    for(auto elem:argVec)
    {
        std::cout<<elem<<std::endl;
    }

}
template<template<typename...>class VecOrSet>
void wrapper(const VecOrSet<int>& input)
{
    //How to avoid this temporary vector if input argument is vector itself
    std::vector<int> temp(input.begin(),input.end());
    print(temp);
}
int main()
{
  std::vector<int> v{1,2,3};  
  std::set<int> s{4,5,6};
  std::unordered_set<int> us{7,8,9};
  wrapper(v);
  wrapper(s);
  wrapper(us);
  return 0;
}

2 个答案:

答案 0 :(得分:4)

您可以添加一个full specialization

template<>
void wrapper(const std::vector<int>& input)
{
    print(input);
}

或者只是添加另一个重载。

void wrapper(const std::vector<int>& input)
{
    print(input);
}

或使用constexpr if(自C ++ 17起)。

template<template<typename...>class VecOrSet>
void wrapper(const VecOrSet<int>& input)
{
    if constexpr (std::is_same_v<VecOrSet<int>, std::vector<int>>) {
        print(input);
    } else {
        std::vector<int> temp(input.begin(),input.end());
        print(temp);
    }
}

答案 1 :(得分:1)

另一种解决方案可以是初始化temp并通过另一个函数getIntVect()(具有通用版本)

template <typename VoS>
std::vector<int> getIntVect (VoS const & input)
 { return { input.cbegin(), input.cend() }; }

input复制到std::vector<int>中,并复制std::version<int>的特定版本

std::vector<int> const & getIntVect (std::vector<int> const & input)
 { return input; }

返回对输入的const引用(即:避免复制)

因此,使用decltype(auto)

template<template<typename...>class VecOrSet>
void wrapper(const VecOrSet<int>& input)
{
   decltype(auto) temp { getIntVect(input) };

   print( temp );
}

temp是对{{1}的引用,当inputVecOrSet时,否则为std::vector的副本。

-编辑-

OP令人怀疑

  

我认为如果输入为vector,这行input将调用vector的副本构造函数

正如T.C.指出的那样,这对decltype(auto) temp{getIntVect(input)};来说是正确的。 (谢谢!)。不适用于auto temp{getIntVect(input)};

尝试编译并运行以下代码

decltype(auto) temp{getIntVect(input)};

我从g ++和clang ++得到了这个输出

#include <iostream>

struct A
 {
   A ()
    { std::cout << "- default constructor" << std::endl; }

   A (A const &)
    { std::cout << "- copy constructor" << std::endl; }

   A (A &&)
    { std::cout << "- move constructor" << std::endl; }
 };

template <typename T>
A foo (T const &)
 { return {}; }

A const & foo (A const & a)
 { return a; }

int main ()
 {
   std::cout << "--- 000" << std::endl;
   A a0;
   std::cout << "--- 001" << std::endl;
   auto           a1 { foo(a0) };
   std::cout << "--- 002" << std::endl;
   decltype(auto) a2 { foo(a0) };
   std::cout << "--- 003" << std::endl;
   decltype(auto) a3 { foo(0) };
   std::cout << "--- 004" << std::endl;
 }

如您所见,--- 000 - default constructor --- 001 - copy constructor --- 002 --- 003 - default constructor --- 004 调用auto a1 { foo(a0) };的副本构造函数(因为A变成auto,而A导致返回值的副本A a1 { foo(a0) };),但foo()不调用构造函数(因为decltype(auto) a2 { foo(a0) };变成decltype(auto),而A const &只是将A const & a2 { foo(a0) };链接到a2 (以foo(a0)开头)。