I am studying the Stroustroup book "C ++ Programming 4th Edition". And I'm trying to follow his example of matrix design.
His matrix class is heavily dependent on templates, and I'm trying my best to figure it out. Here is one of the helper classes for this matrix
A Matrix_slice is part of a Matrix implementation that maps a set of indexes to the location of an element. He uses the idea of generalized slices (§40.5.6):
template<size_t N> struct Matrix_slice { Matrix_slice() = default; // an empty matrix: no elements Matrix_slice(size_t s, initializer_list<size_t> exts); // extents Matrix_slice(size_t s, initializer_list<size_t> exts, initializer_list<siz e_t> strs);// extents and strides template<typename... Dims> // N extents Matrix_slice(Dims... dims); template<typename... Dims, typename = Enable_if<All(Convertible<Dims,size_t>()...)>> size_t operator()(Dims... dims) const; // calculate index from a set of subscripts size_t size; // total number of elements size_t start; // star ting offset array<size_t,N> extents; // number of elements in each dimension array<size_t,N> strides; // offsets between elements in each dimension }; I
Here are the lines that create the subject of my question:
template<typename... Dims, typename = Enable_if<All(Convertible<Dims,size_t>()...)>> size_t operator()(Dims... dims) const;
earlier in the book, he describes how Enable_if and All () are implemented:
template<bool B,typename T> using Enable_if = typename std::enable_if<B, T>::type; constexpr bool All(){ return true; } template<typename...Args> constexpr bool All(bool b, Args... args) { return b && All(args...); }
I have enough information to understand how they work already, and looking at its implementation of Enable_if, I can also output the Convertible function:
template<typename From,typename To> bool Convertible(){
So, I can undersand the building blocks of this template function declaration, but I'm confused when I try to understand how they work. I hope you could help
template<typename... Dims, //so here we accept the fact that we can have multiple arguments like (1,2,3,4) typename = Enable_if<All(Convertible<Dims,size_t>()...)>> //Evaluating and expanding from inside out my guess will be //for example if Dims = 1,2,3,4,5 //Convertible<Dims,size_t>()... = Convertible<1,2,3,4,5,size_t>() = //= Convertible<typeof(1),size_t>(),Convertible<typeof(2),size_t>(),Convertible<typeof(3),size_t>(),... //= true,true,true,true,true //All() is thus expanded to All(true,true,true,true,true) //=true; //Enable_if<true> //here is point of confusion. Enable_if takes two tamplate arguments, //Enable_if<bool B,typename T> //but here it only takes bool //typename = Enable_if(...) this one is also confusing size_t operator()(Dims... dims) const; // calculate index from a set of subscripts
So what do we get in the end? This design
template<typename ...Dims,typename = Enable_if<true>> size_t operator()(Dims... dims) const;
Questions:
- We do not need the second template argument for Enable_if
- Why do we have an assignment ('=') for typename
- What do we get at the end?
Update: You can check the code in the same book that I refer to here C ++ programming language 4th edition on page 841 (Matrix design)