【问题标题】:Initializing a vector of auto (unknown) type inside a template function in C++在 C++ 中的模板函数中初始化自动(未知)类型的向量
【发布时间】:2017-12-26 07:07:06
【问题描述】:

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

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

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

【问题讨论】:

  • 您希望它只与一对迭代器一起工作,还是您可以接受调用者提供所需类型的解决方案?
  • 即使允许,vector&lt;auto&gt; temp(size); 也不包含任何关于编译器auto 应该是什么的线索
  • std::vector&lt;decltype(*beg)&gt;,也许。此外,您不需要distancecopy:只需std::vector&lt;decltype(*beg)&gt; temp(beg, end);
  • 当你进入 C++17 时,auto 可能已经工作了,你可以直接写 std::vector temp(beg, end); 并通过模板推导计算出 auto 一定意味着 iterator_traits&lt;Iter&gt;::value_type
  • 你知道吗:你可以写std::end(bips)而不是std::end(bips)

标签: c++ templates stl iterator


【解决方案1】:

您不能使用 std::vectorauto。你可以改用std::iterator_traits

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

【讨论】:

    【解决方案2】:

    如果您有兼容 C++17 的编译器,您可能会从 class template argument deduction 中受益。

    所以除非你有特定的理由用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);
    

    点赞in Jonathan's answer

    【讨论】:

    • 你忘记了typename,因为value_type 是一个依赖类型。
    【解决方案3】:

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

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

    Demo

    【讨论】:

    • 指针实际上是有效的迭代器。
    • decltype(*beg)T&amp;,它不适用于矢量。
    • 你为什么要创建一个 const int 的向量,只是因为你有一个 const range 输入?
    • 如果*iterator 返回一个代理,这将不起作用,就像三次该死的std::vector&lt;bool&gt;-specialization。
    • @Deduplicator:不起作用,还是给出了意想不到的结果?
    【解决方案4】:

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

    有很多方法可以找出向量的正确类型。您可以使用std::iterator_traits 获取有关迭代器的信息,包括它所引用的值的类型。你会使用typename std::iterator_traits&lt;Iter&gt;::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);
    }
    

    【讨论】:

      【解决方案5】:

      您可以使用iterator_traits 提取指针/iterator 的类型信息。 value_type 是您感兴趣的特定特征,因此您可以这样做:

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

      Live Example

      【讨论】:

      • @tobi303 该评论不再适用。用户更改了答案以使其现在可以工作。现在它是投票最多的答案的重复答案,所以它可能应该被删除。特别是因为它缺少 std:: 来限定名称。
      【解决方案6】:

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

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

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

      • iterator type trait

      如下

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

      【讨论】:

        【解决方案7】:

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

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

        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_ts 正确地将开始结束迭代器耦合在一起。

        现在

        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) 实际上将该范围作为向量返回。

        【讨论】:

          猜你喜欢
          • 2017-05-05
          • 1970-01-01
          • 1970-01-01
          • 2020-04-11
          • 2017-07-11
          • 1970-01-01
          • 2021-11-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多