在C++模板中将’std::vector&’简化为’T&’是否被允许?

huangapple go评论70阅读模式
英文:

Is condensing 'std::vector<T>&' into just 'T&' is permitted in C++ template?

问题

我一直在使用以下对print_vector的重载来打印输入数组的元素([1])。

//////[1]///////
template<typename T>
void print_vector(T& vec) { // this function is for lagacy arrays in C/C++
                            // e.g. int x[3] = {1,2,3};

  std::cout << "- print vector" << std::endl;
  for (const auto& elem : vec) {
    std::cout << elem << std::endl;
  }
}

template<typename T>
void print_vector(std::vector<T>& vec) {
  std::cout << "- print vector" << std::endl;
  for (const auto& elem : vec) {
    std::cout << elem << std::endl;
  }
}

template<typename T>
void print_vector(thrust::host_vector<T>& vec) {
  std::cout << "- print vector" << std::endl;
  for (const auto& elem : vec) {
    std::cout << elem << std::endl;
  }
}

template<typename T>
void print_vector(thrust::device_vector<T>& vec) {
  std::cout << "- print vector" << std::endl;
  for (const auto& elem : vec) {
    std::cout << elem << std::endl;
  }
}

我之所以这样做是因为看起来似乎需要每个函数有两种类型 - 例如,第二种函数需要std::vectorT两种类型。

但我注意到仅仅以下函数[2]就可以为所有类型的输入执行所需的功能。

//////[2]///////

template<typename T>
void print_vector(T& vec) {
  std::cout << "- print vector" << std::endl;
  for (const auto& elem : vec) {
    std::cout << elem << std::endl;
  }
}

乍一看,似乎它过分简化了两种类型为一种T,所以我认为这应该是类似于

//////[3]///////

template<typename T1, typename T2>
void print_vector(T1<T2>& vec) {
  std::cout << "- print vector" << std::endl;
  for (const auto& elem : vec) {
    std::cout << elem << std::endl;
  }
}

在C++中使用[2]函数来表示[1]中的函数重载是标准的吗?还是[2]恰好能正常工作是一种巧合?

仍然让我困惑的事实是,范围for循环for (const auto& elem : vec) {}适用于旧版C/C++数组。当我搜索范围for循环时,我发现这句话 - "defined as anything that you can iterate through—for example, std::vector, or any other C++ Standard Library sequence whose range is defined by a begin() and end()"。尽管指针和迭代器非常相似,但它们并不相同,旧版数组没有可以通过begin()和end()访问的'迭代器',但范围for循环仍然可以工作。为什么?

添加

多亏了273K,我觉得我离答案更近了,但仍然无法理解一些细节。

例如,下面的代码在注释掉vec.begin()部分时可以正常工作。

template<typename T>
void print_vector(T& vec) {
  //std::cout << vec.begin() << std::endl;
  std::cout << "- print vector" << std::endl;
  for (const auto& elem : vec) {
    std::cout << elem << std::endl;
  }
}

int main(void) {

  int x[5] = {1,2,3,4,5};
  print_vector(x);

  return 0;
}

但是当我取消注释时,编译时遇到了以下错误。

main.cu(12): error: expression must have class type but it has type "int *"
          detected during instantiation of "void print_vector(T &) [with T=int [5]]"

所以,你能对你说的话多解释一点吗 - "There are common std::begin(c) and std::end(c) for all containers and T(&)[N]."

英文:

I have been using the following overloading of print_vector to print out elements of the input array ([1]).

//////[1]///////
template&lt;typename T&gt;
void print_vector(T&amp; vec) { // this function is for lagacy arrays in C/C++
                            // e.g. int x[3] = {1,2,3};

  std::cout &lt;&lt; &quot;- print vector&quot; &lt;&lt; std::endl;
  for (const auto&amp; elem : vec) {
    std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

template&lt;typename T&gt;
void print_vector(std::vector&lt;T&gt;&amp; vec) {
  std::cout &lt;&lt; &quot;- print vector&quot; &lt;&lt; std::endl;
  for (const auto&amp; elem : vec) {
    std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

template&lt;typename T&gt;
void print_vector(thrust::host_vector&lt;T&gt;&amp; vec) {
  std::cout &lt;&lt; &quot;- print vector&quot; &lt;&lt; std::endl;
  for (const auto&amp; elem : vec) {
    std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

template&lt;typename T&gt;
void print_vector(thrust::device_vector&lt;T&gt;&amp; vec) {
  std::cout &lt;&lt; &quot;- print vector&quot; &lt;&lt; std::endl;
  for (const auto&amp; elem : vec) {
    std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

The reason why I did like this was that it seemed to require 'two' types for each function - for example, std::vector and T for the second one.

But I noticed that the following function [2] alone can do the desired functions for all types of inputs.

//////[2]///////

template&lt;typename T&gt;
void print_vector(T&amp; vec) {
  std::cout &lt;&lt; &quot;- print vector&quot; &lt;&lt; std::endl;
  for (const auto&amp; elem : vec) {
    std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

At the first glance, it seems that it had overly condensed two types into one T, so I thought this should be something like

//////[3]///////

template&lt;typename T1, typename T2&gt;
void print_vector(T1&lt;T2&gt;&amp; vec) {
  std::cout &lt;&lt; &quot;- print vector&quot; &lt;&lt; std::endl;
  for (const auto&amp; elem : vec) {
    std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

Is using [2] function to represent function overloadings in [1] in C++ is standard? Or is it somehow a coincidence that [2] worked properly?

And the fact still confuses me is that the ranged-for for (const auto&amp; elem : vec) {} is applicable to legacy C/C++ arrays. When I search for the ranged-for, I found this sentence - &quot;defined as anything that you can iterate through—for example, std::vector, or any other C++ Standard Library sequence whose range is defined by a begin() and end()&quot;. Although the pointers and iterators are quite similar, they are not the same and the lagacy arrays do not have 'iterators' that can be accessed by begin(), and end() but still ranged-for works. Why?

ADDED

Thanks to 273K, I feel I am getting closer but still cannot get some points.

For example the following code works fine with the vec.begin() part being commented.

template&lt;typename T&gt;
void print_vector(T&amp; vec) {
  //std::cout &lt;&lt; vec.begin() &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;- print vector&quot; &lt;&lt; std::endl;
  for (const auto&amp; elem : vec) {
    std::cout &lt;&lt; elem &lt;&lt; std::endl;
  }
}

int main(void) {

  int x[5] = {1,2,3,4,5};
  print_vector(x);

  return 0;
}

But when I turn on the commented line, I met the following errors during compilation.

main.cu(12): error: expression must have class type but it has type &quot;int *&quot;
          detected during instantiation of &quot;void print_vector(T &amp;) [with T=int [5]]&quot;

So, could you give a little more explanation on your words - "There are common std::begin(c) and std::end(c) for all containers and T(&)[N]." ?

答案1

得分: 0

以下是要翻译的内容:

这是另一种解释的方式。基于范围的for循环存在的目的只是为了让您编写更简单的循环,以防您不需要索引、指针或迭代器的值。通过std::begin和std::end的魔法抽象,您的示例本质上与此代码相同(https://godbolt.org/z/cMfvPEMvj):

#include <iostream>

void print_vector(int (&vec)[5]) {
  for (const int* elem = vec; elem != vec + 5; ++elem) {
    std::cout << *elem << std::endl;
  }
}

int main() {
  int x[5] = {1,2,3,4,5};
  print_vector(x);
}

它看起来像C和C++的混合。虽然C++允许您创建引用C风格的数组,但这些引用没有成员函数,因为在C中这不是真正存在的事情。所以当您尝试调用一个时,C风格的数组会做最适合它的事情,隐式转换为指针,这仍然不是一个类类型,因此无法通过编译。

英文:

Here is another way of explaining it. The range-based for loop merely exists to let you write simpler loops in case you don't need the value of an index, pointer or iterator. Your example, through the magic of the std::begin and std::end abstractions, is essentially the same as this code:

#include &lt;iostream&gt;

void print_vector(int (&amp;vec)[5]) {
  for (const int* elem = vec; elem != vec + 5; ++elem) {
    std::cout &lt;&lt; *elem &lt;&lt; std::endl;
  }
}

int main() {
  int x[5] = {1,2,3,4,5};
  print_vector(x);
}

Which looks like an eclectic mix of C and C++. Although C++ allows you to create references to C-style arrays, those don't have member functions because that was not really a thing in C. So when you try to call one, the C-style array does what it does best and implicitly converts into a pointer, which is still not a class type and fails the compilation.

huangapple
  • 本文由 发表于 2023年5月14日 09:48:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76245505.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定