variadic template - Elemente einen vector hinzufügen



  • Gibt es eine Möglichkeit alle Elemente hinzuzufügen ohne einen Kontainer zu erstellen?

    #include <iostream>
    #include <vector>
    #include <array>
    
    template<typename T, typename... Args>
    void addAll(std::vector<T>& vec, Args... args)
    {
    	std::array<T, sizeof...(args)> array = { args... };
    	for (const auto& i : array)
    	{
    		vec.push_back(i);
    	}
    }
    
    int main()
    {
    	std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    	addAll(vec, 10, 11, 12, 13, 14, 15);
    
    	for (const auto& i : vec)
    	{
    		std::cout << i << "\n";
    	}
    	std::getchar();
    }
    


  • Ja - Indem du die Funktion rekursiv aufrufst. Um die Rekursion zu stoppen brauchst du einen Rekursionsanker.

    template<typename T>
    void f(std::vector<T>& ) {} // Rekursionsanker - Wenn arguments... ins Nichts expandiert
    
    template<typename T, typename Head, typename... Args>
    void f(std::vector<T>& container, Head&& head, Args&&... arguments){
        container.push_back(std::forward<Head>(head));
        f(container, std::forward<Args>(arguments)...); // head wurde hinzugefügt - fahre fort mit den restlichen Argumenten
    }
    

  • Mod

    struct sequential_exec
    {
        template <typename... T>
        constexpr sequential_exec(T&&...) noexcept {}
    };
    
    template<typename T, typename... Args>
    void addAll(std::vector<T>& vec, Args&&... args)
    {
        // emplace_back gibt eine Referenz zurück, damit erübrigt sich der Kommaoperatorhack
        sequential_exec{ vec.emplace_back( std::forward<Args>( args ) )... };
    }
    

    und in C++17 dann zu

    template<typename T, typename... Args>
    void addAll(std::vector<T>& vec, Args&&... args)
    {
        ( vec.push_back( std::forward<Args>( args ) ), ... );
    }
    


  • Ist das mit dem Rekursionsanker eigentlich weniger effizienter, als das andere? Oder tut das hier nicht zur Sache?
    Ich weiß jetzt nicht, wie ich ASM Code dazu ansehen kann.


  • Mod

    vnoob schrieb:

    Ist das mit dem Rekursionsanker eigentlich weniger effizienter, als das andere?

    Theoretisch nicht. Die Ausdrücke, die Nebeneffekte erzeugen, sind schließlich die Gleichen.
    Praktisch wäre es denkbar, das der Compiler ab einer gewissen Rekursionstiefe das Inlining aufgibt, was sich ggf. (sehr geringfügig) negativ auswirken könnte.



  • camper schrieb:

    struct sequential_exec
    {
        template <typename... T>
        constexpr sequential_exec(T&&...) noexcept {}
    };
    
    template<typename T, typename... Args>
    void addAll(std::vector<T>& vec, Args&&... args)
    {
        // emplace_back gibt eine Referenz zurück, damit erübrigt sich der Kommaoperatorhack
        sequential_exec{ vec.emplace_back( std::forward<Args>( args ) )... };
    }
    

    Das geht so nicht in C++14. In C++14 ist emplace_back void, eine Referenz wird erst in C++17 zurückgegeben. Der ansckließende pack fold Ausdruck für C++17 ist ok.


  • Mod

    Ah, stimmt. Das passiert, wenn man nur in den letzten Draft schaut.
    Also so in C++11 und C++14:

    template<typename T, typename... Args>
    void addAll(std::vector<T>& vec, Args&&... args)
    {
        sequential_exec{ ( vec.push_back( std::forward<Args>( args ) ), 0 )... };
    }
    

Anmelden zum Antworten