At first, I wrote something like this

template<typename T, size_t size>
inline size_t const sizeof_array(T const [size]){return size;}

but if failed, then I find a solution form the other place, like this.

template<typename T, size_t size>
inline size_t const sizeof_array(T const (&)[size]){return size;}

what is the meaning of (&)? Why version one fail?
Thanks a lot

Recommended Answers

All 3 Replies

>>what is the meaning of (&)?

It means that the parameter type is "a reference to a static array", as opposed to just a static array in the first case.

>>Why version one fail?

That's difficult to say. I was aware of this particular problem (it is well known), but it's not super clear why the first version fails. I believe the problem occurs as the compiler tries to find the matching type argument (T), and in that process of converting the parameter to an rvalue (to be passed by value) it figures that the static array should be converted to a pointer (to the first element) and once that is done, it deduces T to be a pointer (say int*) which leads to the parameter not being an array any longer (as it is now just a pointer), that causes the template-argument substitution to fail, and thus eliminating that function template from the set of overload candidates. Essentially, it is a quirky result of this ambiguity in C/C++ between arrays and pointers (because arrays are implicitly converted to pointers). Anyhow, making the parameter a reference rules out the conversion of the array to a pointer, leading to a successful argument deduction of the size value.

As I understand it, in the standard, the array-to-pointer conversion is considered the Lvalue Transformation operation to do on an array. So, the compiler deduces that the function template needs an rvalue, which triggers the conversion of the array to a pointer, which makes the substitution fail. By using a reference parameter, the Lvalue transformation is not done, because the compiler first tries to match the type exactly before resorting to a possible conversion to rvalue and binding a const-reference to it (if your reference is also const).

Thanks, your explanation is quite reasonable.

> At first, I wrote something like this

template<typename T, size_t size>
inline size_t const sizeof_array(T const [size]){return size;}

> but if failed...Why version one fail?


The reason is extremely simple:

1. These three are different ways of declaring the same function - one which takes a single parameter of type int*

void function( int a[100] ) ;
void function( int a[] ) ;
void function( int* a ) ;

2. Top level references are dropped during deduction. Try this:

#include <iostream>
#include <typeinfo>
#include <cxxabi.h> // GNU C++ specific
#include <string>

template< typename T > std::string type_name() // GNU C++ specific
{
    enum { MAX_LEN = 2048 } ;
    char name[MAX_LEN] = "" ;
    std::size_t sz = MAX_LEN ;
    int status ;
    __cxxabiv1::__cxa_demangle( typeid(T).name(), name, &sz, &status ) ;
    return status==0 ? name : "__cxa_demangle error" ;
}

template< typename T > void foo( T arg )
{
    std::cout << "foo - type T is: " << type_name<T>() << '\n' ;
}

template< typename T > void bar( T& arg )
{
    std::cout << "bar - type T is: " << type_name<T>() << '\n' ;
}

int main()
{
    int i = 100 ;
    int& ref = i ;
    foo(ref) ; // type T is: int (top-level reference is dropped)

    int a[100] = {0};
    int (&ref_array)[100] = a ;
    foo(ref_array) ; // type T is: int* (top-level reference is dropped)
    foo<int(&)[100]>(a) ; // type T is: int[100]
    bar(a) ; // type T is: int[100]
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.