最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Templates - Find rank of an array - how templates work - Stack Overflow

programmeradmin3浏览0评论

I found and was debugging the code to find rank of an array - example float[4] array has rank 1. Similarly float[3][2] has rank 2 - where rank means the dimension of an array.

Below is the code using cppinsights:

#include <iostream>

template<typename T>
struct Rank
{
  inline static constexpr const size_t value = 0;
};

/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float[2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[1]>::value;
};

#endif
/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float[1]>
{
  inline static constexpr const size_t value = 1U + Rank<float>::value;
};

#endif
/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float>
{
  inline static constexpr const size_t value = 0;
};

#endif
/* First instantiated from: insights.cpp:18 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float[3][2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[2][1]>::value;
};

#endif

template<typename T, size_t N>
struct Rank<T[N]>
{
  inline static constexpr const size_t value = 1U + Rank<T>::value;
};


int main()
{
  /* PASSED: static_assert(Rank<float[3][2][1]>::value == 3); */
  return 0;
}

The code works fine but I am not able to understand how compiler transforms "Rank<float[3][2][1]>" to "Rank<float[2][1]>" and N as 3 as in below code

template<>
struct Rank<float[3][2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[2][1]>::value;
};

To me it seems more like string regular expression what's the template signature is doing - the initial declaration was:

template <typename T, size_t N> 
struct Rank<T[N]>
{
    static constexpr size_t value = 1U + Rank<T>::value;
};

So this is a template partial specialization and a instantiation of template like Rank<float[3][2][1]> gets mapped to instantiation like:

struct Rank<float[3][2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[2][1]>::value;
};

But how does compiler promotes float[3][2][1] to Rank<float[2][1]> and deduces N as 3 - is it just like template declaration - Rank<T[N]> so Rank<float[3][2][1]> becomes line Rank<float[N][2][1]> - hence N becomes 3 and all other part is T?

I found and was debugging the code to find rank of an array - example float[4] array has rank 1. Similarly float[3][2] has rank 2 - where rank means the dimension of an array.

Below is the code using cppinsights:

#include <iostream>

template<typename T>
struct Rank
{
  inline static constexpr const size_t value = 0;
};

/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float[2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[1]>::value;
};

#endif
/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float[1]>
{
  inline static constexpr const size_t value = 1U + Rank<float>::value;
};

#endif
/* First instantiated from: insights.cpp:13 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float>
{
  inline static constexpr const size_t value = 0;
};

#endif
/* First instantiated from: insights.cpp:18 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Rank<float[3][2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[2][1]>::value;
};

#endif

template<typename T, size_t N>
struct Rank<T[N]>
{
  inline static constexpr const size_t value = 1U + Rank<T>::value;
};


int main()
{
  /* PASSED: static_assert(Rank<float[3][2][1]>::value == 3); */
  return 0;
}

The code works fine but I am not able to understand how compiler transforms "Rank<float[3][2][1]>" to "Rank<float[2][1]>" and N as 3 as in below code

template<>
struct Rank<float[3][2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[2][1]>::value;
};

To me it seems more like string regular expression what's the template signature is doing - the initial declaration was:

template <typename T, size_t N> 
struct Rank<T[N]>
{
    static constexpr size_t value = 1U + Rank<T>::value;
};

So this is a template partial specialization and a instantiation of template like Rank<float[3][2][1]> gets mapped to instantiation like:

struct Rank<float[3][2][1]>
{
  inline static constexpr const size_t value = 1U + Rank<float[2][1]>::value;
};

But how does compiler promotes float[3][2][1] to Rank<float[2][1]> and deduces N as 3 - is it just like template declaration - Rank<T[N]> so Rank<float[3][2][1]> becomes line Rank<float[N][2][1]> - hence N becomes 3 and all other part is T?

Share Improve this question edited Mar 16 at 1:53 Programmer asked Mar 16 at 1:41 ProgrammerProgrammer 8,78924 gold badges93 silver badges180 bronze badges 5
  • @3CxEZiVlQ - corrected as dimension – Programmer Commented Mar 16 at 1:52
  • What is your code, why do you show the instantiated code? Are you aware of if float a[m][n]; then a[0] is float(&)[n]. This is likely used in a real code, a recursive template. – 3CxEZiVlQ Commented Mar 16 at 1:53
  • 1 float[3][2] has rank 2 - where rank means the dimension of an array.” Well, no: the rank is 2, but this is not a dimension. The dimensions are 3 x 2. – Sergey A Kryukov Commented Mar 16 at 2:05
  • Thanks as I understand that float(&a)[n] would means a as reference to the first element i.e a[0] and agree with rank and dimensions of array concept - but how does the template signature gets constructed as float [n] if initially it as passed as float [m][n] - at its recursive call? – Programmer Commented Mar 16 at 2:11
  • 1 en.cppreference/w/cpp/types/rank – NathanOliver Commented Mar 16 at 2:53
Add a comment  | 

3 Answers 3

Reset to default 5

Given:

template <typename T>           // primary
struct Rank {
    static constexpr size_t value = 0;
};

template <typename T, size_t N> // specialization
struct Rank<T[N]> {
    static constexpr size_t value = 1U + Rank<T>::value;
};

Then value is calculated by adding 1 for each match of the specialization:

Rank<float[3][2][1]> matches the specialization in which T is float[2][1] and N is 3.

Rank<float[2][1]> matches the specialization in which T is float[1] and N is 2.

Rank<float[1]> matches the specialization in which T is float and N is 1.

Rank<float> matches the primary.

You seem to be confused by the multidimensional array syntax. The syntax chosen to declare an array is not relevant, so there are no "string regular expression" as you say.

The declaration of the primary template is

template<typename T>
struct Rank
{
  inline static constexpr const size_t value = 0;
};

and the specialization says

template <typename T, size_t N> 
struct Rank<T[N]>
{
    static constexpr size_t value = 1U + Rank<T>::value;
};

The specialization is used whenever the template parameter is an array of size N. It doesn't matter what T is. That T is another array type does not change anything in deduction.

When passed the type float[3][2][1], the compiler sees it as an array of 3 elements, each is a float[2][1]. It doesn't matter what syntax C++ chooses to express such a type. What matters is how the compiler sees it. This happens the moment the compiler parses the type expression, not later when the specialization is checked against.

You may find it easier to understand if the specialization is for std::array<T, N> and the passed type is std::array<std::array<std::array<float, 1>, 2>, 3>. However in the eye of the compiler these two are equally easy to deduce.

You should really ignore the specific syntax chosen to express a type (like the string "float[3][2][1]") as that is irrelevant to the internal representation of the compiler.

I don't think the recursion process is what OP is confused about so I'm not going to elaborate on that.

The recursive definition of Rank is:

    Rank(N) = 0
    Rank(T[Nn]...[N2][N1]) = 1 + Rank([Nn]...[N2])

In C++, this is

template<typename T>
struct Rank
{
  inline static constexpr const size_t value = 0;
};

template<typename T, size_t N>
struct Rank<T[N]>
{
  inline static constexpr const size_t value = 1U + Rank<T>::value;
};
  1. When you call Rank<float[3][2][1]>, T gets float[2][1] and the unused N gets 3.
    So, Rank<float[3][2][1]> calls 1 + Rank<float[2][1]>.
  2. When it calls Rank<float[2][1]>, T gets float[1] and the unused N gets 2.
    So, Rank<float[3][2][1]> calls 1 + 1 + Rank<float[1]>.
  3. When it calls Rank<float[1]>, T gets float and the unused N gets 1.
    So, Rank<float[3][2][1]> calls 1 + 1 + 1 + Rank<float>.
  4. Finally, when it calls Rank<float>, it gets 0.
    So, Rank<float[3][2][1]> get 1 + 1 + 1 + 0 that is 3.
发布评论

评论列表(0)

  1. 暂无评论