在 C++ 的模板函数中初始化自动(未知)类型的向量

Initializing a vector of auto (unknown) type inside a template function in C++

本文关键字:未知 类型 向量 初始化 C++ 函数      更新时间:2023-10-16

我有一个模板函数,我想在其中生成一个未知类型的向量。我试图让它自动,但编译器说这是不允许的。

模板函数获取迭代器或指针,如在跟随的主函数内的测试程序中所示。如何解决问题?

template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
auto size = distance(beg, end);
vector<auto> temp(size); // <--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(beg, end, temp->begin);
.
.
return ....
}

int main()
{
int bips[] = {3, 7, 0, 60, 17}; // Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
vector<int> v = {10, 5, 4, 14}; // Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}

不能使用autostd::vector。你可以改用 std::iterator_traits:

std::vector<typename std::iterator_traits<Iter>::value_type> temp(size);

如果你有一个兼容 C++17 的编译器,你可能会从类模板参数推导中获益。

因此,除非您有特定的原因用std::copy填充向量,否则您可以像这样编写代码:

template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
vector temp(beg, end);
// do the remaining stuff
return ....
}

如果您的编译器上没有此功能,那么我会投票支持

vector<typename iterator_traits<Iter>::value_type> temp(beg, end);

就像乔纳森的回答一样

您可能正在寻找类似的东西

std::vector<typename std::remove_reference<decltype(*beg)>::type> temp(beg, end);

演示

auto

不起作用的原因是因为它在该上下文中是不允许的。您不能提供auto来代替模板参数。当您希望编译器自动推导模板参数时,正确的做法是根本不提供参数。但是,在这种情况下,编译器无法推断出该类型应该是什么。必须显式提供类型。

有很多方法可以找出载体的正确类型。可以使用std::iterator_traits获取有关迭代器的信息,包括它引用的值的类型。您将使用typename std::iterator_traits<Iter>::value_type.

#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <vector>
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw std::domain_error("empty vector");
auto size = std::distance(beg, end);
using t_value = typename std::iterator_traits<Iter>::value_type;
std::vector<t_value> temp(size);
std::copy(beg, end, temp.begin());
return temp;
}

int main()
{
int bips[] = { 3,7,0,60,17 };//Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
std::vector<int> v = { 10,5,4,14 };//Passing iterators of a vector 
auto h = my_func(v.begin(), v.end());
return 0;
}

我想指出的是,没有理由检查 0 尺寸范围。它将正确返回一个空向量。

您还可以利用std::vector有一个接受一对迭代器并复制该范围的构造函数这一事实来简化my_func的主体。

template<class Iter>
auto my_func(Iter beg, Iter end)
{
using t_value =typename std::iterator_traits<Iter>::value_type;
return std::vector<t_value>(beg, end);
}

您可以使用iterator_traits提取指针/iterator的类型信息。value_type是您感兴趣的特定特征,因此您可以执行以下操作:

const vector<typename iterator_traits<Iter>::value_type> temp(beg, end);

现场示例

类型未知是不正确的。要创建的向量类型与Iter类型相同。

只需使用decltype或使用迭代器类型特征获取iter基础类型,如下所示:

  • decltype->std::vector<typename remove_reference<decltype(*beg)>::type> temp(beg, end);

  • iterator type trait

如下

using Type = std::iterator_traits<Iter>::value_type;
std::vector<Type>...

我解决这个问题的方式与您的问题似乎要求略有不同。

首先,我发现范围是比采用两个迭代器更好的基本类型。 两个迭代器是耦合的,它们应该是一个参数。 范围是两个迭代器的简单结构,具有一些实用工具方法:

template<class It>
struct range_t:
std::iterator_traits<It>
{
It b{}, e{};
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin()==end(); }
auto size() const { return std::distance(begin(), end()); }
// etc
range_t()=default;
range_t(range_t const&)=default;
range_t(range_t &&)=default;
range_t& operator=(range_t const&)=default;
range_t& operator=(range_t &&)=default;
};
template<class It>
range_t<It> make_range( It s, It f ) { return {std::move(s), std::move(f)}; }

range_t正确地将开始结束迭代器耦合在一起。

现在

template<class Range>
auto my_func(Range&& range) {
// todo
}
template<class Iter>
auto my_func(Iter beg, Iter end)
{
return my_func(make_range(std::move(beg), std::move(end)));
}

是第一步。 或者完全消除双迭代器版本,并期望调用者为您打包他们的迭代器。

template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
// todo
}

好的,现在您要执行此操作:

auto size = range.size();
vector<auto> temp(size);//<--HERE COMPILER SAYS CANNOT BE AUTO TYPE 
copy(range.begin(), range.end(), temp->begin);

但这是一个常见的操作。 所以我们写它的范围:

template<class Range>
auto as_vector( Range const& r ) {
using value_type = typename Range::value_type;
std::vector<value_type> v( range.begin(), range.end() );
return v;
}

给我们:

template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
auto v = as_vector(range);
// ...
return ...;
}

我们已将您的问题分解为有意义的简单基元,并将实现复杂性转移到这些原语中。my_func的"业务逻辑"不再关心你采取什么步骤将范围变成向量。

这使您的my_func更具可读性,只要您有一些信任,as_vector(range)实际上将该范围作为向量返回。