it-swarm.com.de

Initialisieren eines Vektors mit automatischem (unbekanntem) Typ innerhalb einer Vorlagenfunktion in C++

Ich habe eine Template-Funktion, in der ich einen Vektor erzeugen möchte, der von einem unbekannten Typ ist. Ich habe versucht, es automatisch zu machen, aber der Compiler sagt, dass es nicht erlaubt ist.

Die Vorlagenfunktion ruft entweder Iteratoren oder Zeiger auf, wie im Testprogramm innerhalb der folgenden Hauptfunktion dargestellt. Wie kann das Problem behoben werden?

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

Sie können keinen std::vector von auto verwenden. Sie könnten stattdessen std :: iterator_traits verwenden:

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

Wenn Sie einen C++ 17-kompatiblen Compiler haben, können Sie von Klassenvorlagenargumenten profitieren.

Wenn Sie also keinen bestimmten Grund haben, Ihren Vektor mit std::copy zu füllen, können Sie Ihren Code folgendermaßen schreiben:

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

Wenn diese Funktion auf Ihrem Compiler nicht verfügbar ist, würde ich dafür stimmen

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

wie in Jonathan Antwort

32
Vasiliy Galkin

Sie könnten nach etwas suchen

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

Demo

7
Igor Tandetnik

Der Grund, dass auto nicht funktioniert, ist, dass dies in diesem Kontext nicht zulässig ist. Sie können statt eines Vorlagenarguments nicht auto angeben. Die richtige Vorgehensweise, wenn der Compiler ein Vorlagenargument automatisch ableiten soll, besteht darin, überhaupt kein Argument anzugeben. In diesem Fall kann der Compiler jedoch nicht ableiten, was dieser Typ sein soll. Sie mussmuss den Typ explizit angeben.

Es gibt viele Möglichkeiten, den für Ihren Vektor richtigen Typ herauszufinden. Sie können std::iterator_traits verwenden, um Informationen zu einem Iterator zu erhalten, einschließlich des Wertetyps, auf den er verweist. Sie würden typename std::iterator_traits<Iter>::value_type verwenden.

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

Ich möchte darauf hinweisen, dass es keinen Grund gibt, nach 0 Größenbereichen zu suchen. Es würde korrekt einen leeren Vektor zurückgeben.

Sie können den Hauptteil von my_func auch ein wenig vereinfachen, indem Sie die Tatsache ausnutzen, dass std::vector über einen Konstruktor verfügt, der ein Paar von Iteratoren akzeptiert und diesen Bereich kopiert.

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

Mit iterator_traits können Sie die Typinformationen eines Zeigers/iterator extrahieren. value_type ist das spezifische Merkmal, an dem Sie interessiert sind, sodass Sie Folgendes tun können:

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

Live Example

2
Jonathan Mee

Es ist nicht wahr, dass der Typ nicht bekannt ist. Der Typ des Vektors, den Sie erstellen möchten, ist vom Typ Iter.

Holen Sie einfach iter den zugrunde liegenden Typ entweder mit decltype oder mit iterator type trait wie folgt:

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

  • iterator type trait

wie folgt

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

Ich würde das etwas anders lösen, als Ihre Frage scheint.

Erstens finde ich, dass Bereiche ein besserer Grundtyp sind als zwei Iteratoren. Die beiden Iteratoren sind gekoppelt, sie sollten ein Argument sein. Ein Bereich besteht aus einer einfachen Struktur aus zwei Iteratoren mit einigen Hilfsmethoden:

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 koppelt den Iterator Anfang und Ende korrekt zusammen.

Jetzt

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

ist der erste Schritt. Oder entfernen Sie einfach die Version mit zwei Iteratoren vollständig und erwarten Sie, dass der Aufrufer die Iteratoren für Sie paketiert.

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

Ok, jetzt möchtest du das tun:

auto size = range.size();

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

aber das ist eine übliche Operation. Also schreiben wir es für die Reichweite:

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

geben uns:

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

Wir haben Ihr Problem in einfache Grundelemente mit Bedeutung zerlegt und die Implementierungskomplexität in diese Grundelemente verschoben. Die "Geschäftslogik" Ihres my_func kümmert sich nicht mehr darum, welche Schritte Sie unternehmen, um aus einem Bereich einen Vektor zu machen.

Dies macht Ihren my_func more lesbar, solange Sie etwas Vertrauen haben, dass as_vector(range) diesen Bereich tatsächlich als Vektor zurückgibt.