web-dev-qa-db-fra.com

Initialisation d'un vecteur de type auto (inconnu) dans une fonction de modèle en C++

J'ai une fonction template à l'intérieur de laquelle je veux générer un vecteur de type inconnu. J'ai essayé de le rendre automatique, mais le compilateur dit que ce n'est pas autorisé.

La fonction de modèle obtient des itérateurs ou des pointeurs, comme le montre le programme de test dans la fonction principale suivie. Comment le problème peut-il être résolu?

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

Vous ne pouvez pas utiliser un std::vector de auto. Vous pouvez utiliser std :: iterator_traits à la place:

std::vector<typename std::iterator_traits<Iter>::value_type> temp(size);
42
Edgar Rokjān

Si vous avez un compilateur compatible C++ 17, vous pouvez bénéficier de déduction d'argument de modèle de classe .

Donc, à moins que vous n'ayez une raison spécifique de remplir votre vecteur avec std::copy, vous pouvez écrire votre code de la manière suivante:

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

Si cette fonctionnalité n'est pas disponible sur votre compilateur, je voterais pour

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

comme dans la réponse de Jonathan

32
Vasiliy Galkin

Vous pourriez être à la recherche de quelque chose comme

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

Démo

7
Igor Tandetnik

La raison pour laquelle auto ne fonctionne pas est que ce n'est pas autorisé dans ce contexte. Vous ne pouvez pas fournir auto à la place d'un argument de modèle. Lorsque vous voulez que le compilateur déduise automatiquement un argument de modèle, il convient de ne pas fournir d'argument du tout. Cependant, dans ce cas, le compilateur n'a aucun moyen de déduire ce que ce type devrait être. Vous devez fournir le type explicitement.

Il existe de nombreuses façons de déterminer le type correct pour votre vecteur. Vous pouvez utiliser std::iterator_traits pour obtenir des informations sur un itérateur, y compris le type de valeur auquel il fait référence. Vous utiliseriez 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;
}

Je tiens à souligner qu’il n’ya aucune raison de vérifier la taille 0. Cela retournerait correctement un vecteur vide.

Vous pouvez également simplifier un peu le corps de my_func en tirant parti du fait que std::vector a un constructeur qui accepte une paire d’itérateurs et copie cette plage.

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);
}
3
François Andrieux

Vous pouvez extraire les informations de type d'un pointeur/iterator à l'aide de iterator_traits . value_type est le trait spécifique qui vous intéresse, ainsi vous pouvez faire:

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

Live Example

2
Jonathan Mee

Ce n'est pas vrai que le type n'est pas connu. Le type de vecteur que vous souhaitez créer est du même type que Iter.

Procurez-vous simplement iter Type sous-jacent, soit en utilisant decltype, soit en utilisant le type trait de type - iterator comme suit:

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

  • iterator type trait

comme suit

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

Je résoudrais cela légèrement différemment de ce que votre question semble demander.

Premièrement, je trouve que les gammes sont un type fondamental meilleur que de prendre deux itérateurs. Les deux itérateurs sont couplés, ils devraient constituer un seul argument. Une plage est une structure simple de deux itérateurs, avec quelques méthodes d’utilité:

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 couple correctement l'itérateur begin end.

À présent

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

est la première étape. Ou alors, éliminez simplement la version à deux itérateurs et attendez-vous à ce que l'appelant emballe ses itérateurs pour vous.

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

Ok, maintenant vous voulez faire ceci:

auto size = range.size();

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

mais c'est une opération commune. Nous écrivons donc pour la gamme:

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

nous donnant:

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

Nous avons décomposé votre problème en simples primitives ayant un sens et transféré la complexité de la mise en œuvre à ces primitives. La "logique métier" de votre my_func ne tient plus compte des étapes à suivre pour transformer une plage en vecteur.

Cela rend votre my_func more lisible, tant que vous avez la certitude que as_vector(range) renvoie réellement cette plage sous forme de vecteur.

0