Search code examples
c++metaprogrammingtype-deduction

why type deduction does not work as expected?


I have a small question about type deduction in C++ metaprogramming. There is a certain function do some action.

main.cpp

template<typename T> void foo(T arg) {
    // do some action on argument
    std::cout << typeid(arg).name() << std::endl;
}


int main(int argc, char** argv) {
    int array[100] = {0};
    std::cout << typeid(array).name() << std::endl;
    foo(array);

    return 0;
}

Output:

A100_i
Pi     

Why arg in function foo() have a another data type than array in function main()?


Solution

  • Because C style arrays are broken. In particular, you cannot have a function argument with a C style array type; if you write a function (forgetting about templates for the moment):

    void foo( int arg[100] );
    

    the language requires the compiler to treat this as:

    void foo( int* arg );
    

    (and the 100 is just a comment—it is ignored by the compiler).

    In order to support this in the case of templates, if the compiler is trying to match a non-reference template argument, it will convert an array argument to a pointer, and type deduction will result in the pointer type, not the array type.

    The result is that you should never write a function (template or otherwise) expecting a C style array (except for the second argument of main, where you don't have a choice).

    Since this brokeness is only present for reasons of C compatibility, C++ doesn't follow it when references are involved. So:

    template < typename T, size_t N >
    void foo( T (&arg)[ N ] );
    

    will work, and should give you the same results in both cases. If you think that your function might be called with both C style arrays and other things (e.g. std::vector), you can overload it for both. The version above is more specialized, and will be preferred to the more generic version if possible.

    A better solution would be to avoid C style arrays entirely, but they are useful for static variables with initialization; it's only with C style arrays that you can get the compiler to count the number of elements, and define the size of the array according to the initializer list. And have static initialization; std::vector will count the initializers at runtime, but used as a static variable, may cause order of initialization problems. C style arrays and