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;
}
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);
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);
Vous pourriez être à la recherche de quelque chose comme
std::vector<typename std::remove_reference<decltype(*beg)>::type> temp(beg, end);
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);
}
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);
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>...
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_t
s 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.