0

Here is a piece of code

// check if type of static array
template <class T>
struct ABIStaticArray : std::false_type  //#1
{
};

// stringN type => bytesN
template <std::size_t N>
struct ABIStaticArray<std::array<char, N>> : std::false_type  //#2
{
};

// a fixed-length array of N elements of type T.
template <class T, std::size_t N>
struct ABIStaticArray<std::array<T, N>> : std::true_type //#3
{
};

// fixed length of type, default 1 except static array type
template <class T, class Enable = void>
struct Length                                 //#4
{
    enum
    {
        value = 1
    };
};

// length of static array type
template <class T>
struct Length<T, typename std::enable_if<ABIStaticArray<T>::value>::type>     //#5
{
    enum
    {
        value = std::tuple_size<T>::value * Length<typename std::tuple_element<0, T>::type>::value
    };
};
  1. Why does the std::enable_if in specialized template of Length only have the first parameter?
  2. If I pass in a StaticArray, how does it match the specialized template of Length, if the value of first param of std::enable_if in specialized template of Length is true, the specialized template will become:
template <class T>
struct Length<T, void>
{
    enum
    {
        value = std::tuple_size<T>::value * Length<typename std::tuple_element<0, T>::type>::value
    };
};

why need this specialized template, what is the difference with the basic template of Length? the call of Length likes:

Length<std::array<int, 5>>::value

the result is 5

Please help, thanks very much!

Jason
  • 36,170
  • 5
  • 26
  • 60
lake
  • 49
  • 5
  • 1
    The second parameter is defaulted to `void`, see https://en.cppreference.com/w/cpp/types/enable_if. In most cases one doesn't care about the actual type produced (except in return type SFINAE), so that is actually the more common form to see... – user17732522 Oct 21 '22 at 09:30
  • And for the other question: You are not showing any of the involved types, so it is impossible to know. The decision is made somehow by the `ABIStaticArray::value && false == ABIDynamicType::value` expression and the involved classes. (`false ==` is pretty weird btw.) – user17732522 Oct 21 '22 at 09:33
  • See also [Template default arguments](https://stackoverflow.com/questions/15373823/template-default-arguments) – Jason Oct 21 '22 at 09:51
  • @user17732522u hi, the code has been completed and made some small changes – lake Oct 21 '22 at 09:52
  • @JasonLiam thanks, The first problem is that I'm not very familiar with std::enable_if, Can you help with the second question? – lake Oct 21 '22 at 09:55
  • @lake Sure, but can you also add the calling code? Just add the main function and how you want to use these templates and which call you're asking about. That will also make your question more concise. As currently if you're just asking about how enable_if works, then there are many related SO posts. The answer will also depend on how exactly use `Length`. Just add some examples of using it in your question. – Jason Oct 21 '22 at 09:58
  • @JasonLiam ok, I completed it, like the call, if I pass std::array, how does it match the specialized template of Length? – lake Oct 21 '22 at 10:11

1 Answers1

2

Let's see what is happening step by step. When you wrote:

Length<std::array<int, 5>>::value

Step 1) The template parameter T for Length is std::array<int, 5>

Step 2) Now there are two options that can be used. Either the primary template template <class T, class Enable = void> labeled as #4 or the specialization for static array #5. We want to find which one should be used. For this the condition typename std::enable_if<ABIStaticArray<std::array<int, 5>>::value>::type is tested.

Step 3) Now, ABIStaticArray<std::array<int, 5>> uses the 2nd specialization labeled as #3 because it is more specialized than the first specialization #2 and from the primary template #1.

Step 4) Since the second specialization is used, std::enable_if<ABIStaticArray<std::array<int, 5>>::value evaluates to true so that typename std::enable_if<ABIStaticArray<std::array<int, 5>>::value>::type evaluates to void. Basically, the specialization of Length is preferred over the primary template.

Step 5) Since the specialization of Length is selected the value is calculated using value = std::tuple_size<std::array<int, 5>>::value * Length<typename std::tuple_element<0, std::array<int, 5>>::type>::value which comes out to be 5.

To confirm this I modified your program slightly:

// length of static array type
template <class T>
struct Length<T, typename std::enable_if<ABIStaticArray<T>::value>::type>  //#5
{
    enum
    {
        value = std::tuple_size<T>::value * Length<typename std::tuple_element<0, T>::type>::value
    };
    Length()
    {
        std::cout <<"specialization #5"<<std::endl;
    }
};

int main()
{
    Length<std::array<int, 5>> v; //will use the specialization of Length
}

Working demo.

The output of the program is:

specialization #5
Jason
  • 36,170
  • 5
  • 26
  • 60
  • Thanks very much. from your answer I understand that even though the parameters are the same, the specialized template is still preferred. – lake Oct 21 '22 at 13:52
  • @lake Yes, we say that it is *"more specialized"* than the rest of them. You can look up that term *"more specialized"* and there will be many posts explaining that in more detail. You're welcome. For example, [When is a template more specialized than the other?](https://stackoverflow.com/questions/15702143/when-is-a-template-more-specialized-than-the-other-and-or-confusion-with-lo). – Jason Oct 21 '22 at 13:57