3
votes

I have found a code in https://stackoverflow.com/a/36132696/3206356 and I try it. It works, but I don't fully understand what happens there.

I have duplicated code from that link below:

template <size_t N, class = std::make_index_sequence<N>>
class Vector;

template <size_t N, size_t... Is>
class Vector<N, std::index_sequence<Is...>> 
{
private:
    std::array<double, N> vals;

    template <size_t >
    using double_ = double;
public:
    Vector(double_<Is>... vals)
    {
        ...
    }
};

For example, we try to use it next way:

Vector<3> a(1.0, 2.0, 3.0);

How type deduction works here?

p.s. As I understand when the compiler sees that line, first of all, it tries to deduce types for specialization. It deduces N as 3 and Is as empty sequence and then fails when can't find appropriate constructor. General template is not defined, so compilers must fail here too. But what happens next?

1
No, it deduces N as 3 and then the second argument as std::make_index_sequence<3>, because that's what the default template parameter says. Is... can never be an empty sequence (unless N is 0).n. 1.8e9-where's-my-share m.
But N from the general template and N from specialization are different. When I wrote that N is deduced as 3, I meant for specialization. And as I understand when we deduce types for specialization we know nothing about std::make_index_sequence<N>. Am I wrong?akulinich
It works like this. The compiler sees Vector<3>. It goes to the main template, deduces the parameters it can, and fills arguments that have default values. This turns Vector<3> into Vector<3, std::make_index_sequence<3>>. Then a suitable specialisation is found for this instantiation and the parameters are deduced again.n. 1.8e9-where's-my-share m.
I got it now! Thanks! =)akulinich

1 Answers

1
votes

This part declares (not defines) the default specialisation of the template class Vector. The second template argument defaults to an index sequence of 0...N-1

template <size_t N, class = std::make_index_sequence<N>>
class Vector;

The default argument is important as it serves to present a simple interface and hide the complexities of the next specialisation...

This specialisation is the one instanciated as a result of the above default declaration. The purpose of the index sequence is to carry the variadic sequence of Is (i.e. 0 ... N-1).

template <size_t N, size_t... Is>
class Vector<N, std::index_sequence<Is...>> 
{

Defines sufficient storage

private:
    std::array<double, N> vals;

Provides a means of translating on of the sequence Is from a size_t into a type (in this case, double)

    template <size_t >
    using double_ = double;

public:

Defines the constructor to take double_<0>, double_<1> ... double_<N-1>. But double<N> for any N is a typedef of double. So what this line is doing is providing a constructor which takes one double for each Is. i.e. exactly as many doubles as required to build the array. It's pretty clever actually.

    Vector(double_<Is>... vals)
    {
        ...
    }
};