C++ Function Template Formatting

半世苍凉 提交于 2021-02-05 06:12:05

问题


Just had a question about class templates:

For the following code, the function runs totally fine, but I am confused as to why/how you can run the fill function without giving a class/type for the iterators (why you don't need to supply the iterator type):

#include <vector>
#include <iostream>
#include <typeinfo>

template<typename Iter>
void fill(Iter first, Iter limit, int value){
  while (first != limit) {
    *first = value;
    ++first;
  }
}
int main() {
  std::vector<int> vec1 {2, 4, 6, 1, 9};
  fill(vec1.begin(), vec1.end(), 7);
}

Regardless, my actual question is the following:

I just wanted to do template <typename Iter, typename A>, so I could specify the datatype of the value variable in the function (rather than having to name int explicitly in the function template)

However, when I try fill<int>(...) with this train of thought, the program doesn't compile correctly at all:

#include <vector>
#include <iostream>
#include <typeinfo>

template<typename Iter, typename A>
void fill(Iter first, Iter limit, A value){
  while (first != limit) {
    *first = value;
    ++first;
  }
}
int main() {
  std::vector<int> vec1 {2, 4, 6, 1, 9};
  fill<int>(vec1.begin(), vec1.end(), 7);
}

Basically I just was curious how you could change the template to allow for a single fill<datatype of value>(arg1,arg2...) function that still accepts the iterators correctly without explicitly naming them.

Thanks!

edit: Ended up using ::fill! Thank you everyone for the knowledge and information!


回答1:


Your actual question

You're calling your second function template wrong. If you want to specify a specific template parameter you must specify each parameter before the one you really want to specify too.

e.g.

fill<std::vector<int>::iterator, int>(std::begin(vec), std::end(vec), 5);

Also even then your code won't compile.

The reason why your second prototype doesn't compile is because ADL is finding another function named fill within the std namespace that has an identical prototype to yours and can't decide whether to use your version or the one within std. To fix this simply call fill like this:

::fill(vec.begin(), vec.end(), 5);

Which is explicitly calling the fill in the global namespace.


Your first question:

A function template is called that because it is literally a template that will be used for a real function the compiler will, or you will explicitly instantiate.

The compiler figures out the type of the arguments at the calling site and calls the right instantiation of the function template based on that.

It is analogous to function argument overloading.

E.g. this:

template<typename T>
auto fn(T arg){
    std::cout << arg << std::endl;
}

auto main() -> int{
    fn(1); // compiler sees you pass an int
    fn(1.0); // compiler sees you pass a double
}

is the same as this:

auto fn(int i){
    std::cout << i << std::endl;
}

auto fn(double d){ // heh, double d
    std::cout << d << std::endl;
}

auto main() -> int{
    fn(1);
    fn(1.0);
}



回答2:


The reason it doesn't work because you are specifying the first template argument Iter as an int, but it's not the type of the iterator, hence the error.

You have to specify a template argument because if you don't, Argument Dependent Lookup will find std::fill with the same signature, and there is an ambiguity.

What you could do to fix it:

  • Change the order of the template arguments, so Iter gets deduced and you can specify A: template<typename A, typename Iter>
  • Explicitly state that you want your function (not the std:: one) with the score resolution operator: ::fill(vec1.begin(), vec1.end(), 7);



回答3:


I just wanted to do template , so I could specify the datatype of the value variable in the function (rather than having to name int explicitly in the function template)

What you really wanted (comments inline):

#include <iterator>
#include <vector>


template<class Iter>
Iter myfill(
  // deduce template argument from this
            Iter first,

  // and this
            Iter last,  

  // std::iterator_traits<>::value_type deduces the value type that the
  // iterator is iterating over - i.e. what it's 'pointing at'
  // we need typename because iterator_traits<Iter> is dependent on the deduction
  // of Iter
            const typename std::iterator_traits<Iter>::value_type& v)
{
  while (first != last) {
    *first++ = v;
  }
  return first;
}

int main()
{
  std::vector<int> v1(20);
  myfill(std::begin(v1), std::end(v1), 20);

  myfill(std::begin(v1), 
         std::end(v1), 
         20.0); // NOTE: conversion will happen at the call site
  // myfill<std::vector<int>::iterator> will still be selected

}



回答4:


I guess u need to pass also Iterator as argument like

std::vector< int >::iterator 

Because template expects that Plus it would be then ambiguous call because already fill method exists within std::vector namespace What you can do is rename that fill method as myfill and it works ;)



来源:https://stackoverflow.com/questions/36660394/c-function-template-formatting

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!