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