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

Different behavior needed if template template parameter is vector

本文关键字:参数 如果      更新时间:2023-10-16

我们正在使用一个内部 c++ 库,它将std::vector作为输入,但是我想编写应该能够接受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;
}

您可以添加完整的专业化。

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

另一种解决方案可能是temp通过另一个函数进行初始化:getIntVect(),具有通用版本

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

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

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

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

因此,使用decltype(auto)

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

temp是对input的引用,当VecOrSetstd::vector时,或者input的副本,否则。

--编辑--

OP 是可疑的

我认为如果输入是矢量,这一行decltype(auto) temp{getIntVect(input)};将调用矢量的复制构造函数

正如T.C.所指出的,auto temp{getIntVect(input)};也是如此(谢谢!不适合decltype(auto) temp{getIntVect(input)};

尝试编译并运行以下代码

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

我得到(来自 g++ 和 clang++)这个输出

--- 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)(因此a0)。

相关文章: