Variadic Template Used in StringArray Constructor

Hi, this is more of a C++ question that uses a JUCE example.

I am trying to understand the operation of variadic templates. I understand that they allow a function to take a variable number of arguments in a type safe way. However, I don’t understand how they do this.

StringArray uses a variadic template in one of its constructor overloads:

template <typename... OtherElements>
StringArray (StringRef firstValue, OtherElements... otherValues) : strings (firstValue, otherValues...) {}

This will only allow certain types to be used as arguments. From relating what this article says to this case I thought that the complier would determine what types could be used by seeing what types could be used as constructors for the String class. However this is not the case since the String class can take an int as an argument in its constructor and here:
std::string tempString("test2");
StringArray temp("Test", tempString, 3);

The 3 causes a compilation error. Could someone please explain this to me?

The error is

cannot convert argument 1 from 'int' to 'const ElementType &'
Constructor for class 'juce::String' is declared 'explicit'

and, indeed

explicit String (int decimalInteger);

This has nothing to do with the variadic template itself. You mark a constructor as explicit to avoid implicit conversions like this. This should compile fine:

std::string tempString("test2");
StringArray temp("Test", tempString, String(3));

I see, so how is the complier determining what is allowed in the variadic template. Does it only allow strings because the argument before the variadic template is a string or is it because as soon as they go into the variadic template they are added into an Array called strings?

A type template parameter allows any type. Constraints on template parameters are a new feature of C++20. In this case, the type is substituted without any check. It goes through the whole call chain until it reaches this in ArrayBase:

template <typename... OtherElements>
void addAssumingCapacityIsReady (const ElementType& firstNewElement, OtherElements... otherElements)
    addAssumingCapacityIsReady (firstNewElement);
    addAssumingCapacityIsReady (otherElements...);

Here, each element has to be converted to ElementType -in this case, String. If there’s no conversion available, you get an error.