Variadic templated method to extract values from a table Is this the correct, most sane way to do variadic templates in C++?

All that this function does is to see if gate has a key, if it has, it adds it to a vector of values, if not, it default constructs an object in place.

This function belongs to a templated class called core_table where K is the key type and V the value type.

template &lt;typename ...Args&gt;
std::vector&lt;V&gt; values_at(Args const &amp; ...args) const {
    std::initializer_list&lt;K&gt; input {args...};
    std::vector&lt;V&gt; output;
    output.reserve(input.size());
    for (auto const &amp; e : input) {
        auto const it = gate.find(e);
        if (it != gate.end()) {
            output.emplace_back((*it)-&gt;second);
            continue;
        }
        output.emplace_back();
    }
    return output;
}


I'm asking it because all the examples of variadic templates that I saw until now made use of recursion. I'm still in a state of desbelief that I can just write std::initializer_list&lt;K&gt; input {k, args...}; and iterate over it, I literally didn't knew until now that this was possible - this does make variadic templates much more approachable in C++, at least for me.

So, is this a good example? How can I make it better, more optimized?

Working Example

#include &lt;iostream&gt;
#include &lt;list&gt;
#include &lt;set&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

using std::cout;
using std::endl;

template &lt;typename K, typename V&gt;
class core_table {
    using value = std::pair&lt;K, V&gt;;
    using chain = std::list&lt;value&gt;;

    using iterator = typename chain::iterator;
    using const_iterator = typename chain::const_iterator;

    struct compare {
        using is_transparent = void;

        bool operator ()(const_iterator x, const_iterator y) const {
            return x-&gt;first &lt; y-&gt;first;
        }

        bool operator ()(const_iterator x, K const &amp; k) const {
            return x-&gt;first &lt; k;
        }

        bool operator ()(K const &amp; k, const_iterator y) const {
            return k &lt; y-&gt;first;
        }
    };

    chain data;
    std::set&lt;iterator, compare&gt; gate;

public:
    core_table() = default;

    core_table(core_table &amp;&amp; ct) = default;

    core_table(core_table const &amp; ct) {
        for (auto const &amp; e : ct) {
            push(e.first, e.second);
        }
    }

    core_table(std::initializer_list&lt;value&gt; il) {
        for (auto const &amp; e : il) {
            push(e.first, e.second);
        }
    }

    template &lt;typename Key, typename Value&gt;
    core_table &amp; insert(const_iterator cit, Key &amp;&amp; k, Value &amp;&amp; v) {
        auto const it = gate.find(k);
        if (it != gate.end()) {
            (*it)-&gt;second = std::forward&lt;Value&gt;(v);
            return *this;
        }
        gate.emplace(data.emplace(cit, std::forward&lt;Key&gt;(k), std::forward&lt;Value&gt;(v)));
        return *this;
    }

    template &lt;typename Key, typename Value&gt;
    core_table &amp; push(Key &amp;&amp; k, Value &amp;&amp; v) {
        return insert(data.end(), std::forward&lt;Key&gt;(k), std::forward&lt;Value&gt;(v));
    }

    template &lt;typename ...Args&gt;
    std::vector&lt;V&gt; values_at(Args const &amp; ...args) const {
        std::initializer_list&lt;K&gt; input {args...};
        std::vector&lt;V&gt; output;
        output.reserve(input.size());
        for (auto const &amp; e : input) {
            auto const it = gate.find(e);
            if (it != gate.end()) {
                output.emplace_back((*it)-&gt;second);
                continue;
            }
            output.emplace_back();
        }
        return output;
    }
};

int main() {

    core_table&lt;std::string, std::string&gt; ct {{"a", "hello"}, {"b", "world"}};

    for (auto const &amp; e : ct.values_at("b", "a")) {
        cout &lt;&lt; e &lt;&lt; endl;
    }

    return 0;
}
Yes, it is a good example

Variadic templates are associated with recursion by similarity to functional programming, where an recursion is a simple method of operating on sequences. Direct expansion is akin to a map or fold, or some other higher-order function.

It can be simplified even more

If you have a similar function 

/* find value or return default */
template &lt;typename Arg&gt; 
V value_at(Arg const &amp; key) const 
{
    auto const it = gate.find(key);
    if (it != gate.end()) {
        return it-&gt;second;
    }
    return {};
}


Then you can condense this down into a single expression

return vector&lt;V&gt;({value_at(args)...});


as that expands the parameter pack into multiple calls to value_at, which goes into vector's std::initializer_list&lt;V&gt; constructor.