Search code examples
c++templatesvariadic-templates

Can someone help me enlighten this template?


I know that the title sucks, it's just that I wasn't really sure how else to ask this, because I'm lacking knowledge in many basic C++ features and I'm trying to address them one by one.

So I'm starting to dive into C++ templates. For now I'm building really basic ones, simply to help me debug my code, like:

    void printVectors(const T& vec, const S& vec2) {
        for (int i = 0; i < vec.size(); i++) {
            print("------------");
            printNoEndln(vec[i]);
            printNoEndln("->");
            print(vec2[i]);
        }
    }

Now, starting with SDL, I came across something like this:

    template <typename T, typename... TArgs>
    T& AddComponent(TArgs&&... args) {
        T* newComponent(new T(std::forward<TArgs>(args)...));
        newComponent->componentName = typeid(T).name();
        newComponent->owner = this;
        components.emplace_back(newComponent);
        newComponent->Init();
        return *newComponent;
    }

So I'm going to describe what I understand by this line by line, and I'd be really glad if you guys could correct me.

1- Creating a template of type T with N arguments (Thats what the spread operator means here, right? It declares a variadic template?)

2- Declaring a function AddComponent that returns the reference of T and receiving N references of references of the type TArgs (???) (This part is really confusing to me).

3- Declaring a pointer T to a new object of type T in a very strange way. For starters, I don't understand why the instantiation of newComponent is being addressed with () such as newComponent() instead of T* newComponent = new Component(...)(Seriously, that is all that I can grasp from this line)

From line 4 and forward the code is very simple to me and needs no explanation whatsoever.

From what I could understand, my gap is not being able to understand: variadic templates and functions, rvalues, lvalues and the forward problem. Is that correct? Can you guys point me to useful articles regarding these topics? I find most of cpp references very formal and way beyond my league. It's funny how I can read C#, Java, Typescript and Javascript documentations with no problem but when it comes to C++ it gets really tough to extract something from the documentations. I usually just skip the formality to the actual example code and then I try it out myself on the compiler to understand what is happening.

I know it's a lot, answer with whatever information you judge useful.

Thanks guys


Solution

  • template <typename T, typename... TArgs>
    T& AddComponent(TArgs&&... args)
    

    That's a declaration of a function template AddComponent. T and TArgs are template arguments, both of which must be types. TArgs is a variadic argument (can hold 0 or more types). A function template stamps out functions for each unique combination of T and TArgs, for ex. AddComponent<int,long,float> could be such a function.

    TArgs&&... are called forwarding references because && inside a template declaration has an effect of preserving the value category (lvalue/rvalue) of the argument(s).

        T* newComponent(new T(std::forward<TArgs>(args)...));
    

    That's a definition of a local variable newComponent of type pointer-to-T and initial value new T(std::forward<TArgs>(args)...).

    std::forward is a helper utility that works together with TArgs&& and when needed passes rvalue references through to the call. When browsing code you can ignore it and read new T(std::forward<TArgs>(args)...) as new T(args...). Now that is a variadic pack expansion syntax; here the arguments args from the template are simply passed to new().

    For more details refer to your favorite C++ Book. My favorite (years ago) was Effective Modern C++ by Scott Meyers (Scott calls forwarding references universal references, for the rest is a great book).