Search code examples
c++templatestypeidmost-vexing-parse

Finding typeid of a template parameter


The print statement in the constructor's definition doesn't get printed, isn't the constructor calling correct in main? I know I am missing some point here, please point out.

#include <iostream>
#include <typeinfo>

template <typename T> class List
{
    public: 
        template <typename T2> List (List<T2> const&);
}; 

template <typename T> template <typename T2> List <T> :: List (List <T2> const&) 
{
    std :: cout << "\nType name:" << typeid (T2).name();
}

int main ()
{
    List <int> kk (List <int>);
    return 0;
}

Solution

  • There are a couple of things wrong in your code that you might not be aware of.

    List<int> kk( List<int> );
    

    That line is not a variable definition, but rather the declaration of a function that takes a List<int> as argument and returns a List<int>, so that effectively will not call any constructor. That is know as the most-vexing-parse (you can look at different versions of it by searching in SO, or in the C++ FAQ lite)

    The second issue is that you cannot possibly create any instance of the an instantiated type of List, the reason being is that the only constructor that you are providing is a templated constructor that takes a second List<U> as argument. That effectively disables the default constructor, so the only way of creating a List<T> is by already having a List<U>, and that is not possible. You can add the default constructor back:

    template <typename T>
    class List {
    public:
       List() {}
       template <typename U>
       List( List<U> const & ) {} // prefer const& as that will avoid unnecessary copying
    };
    

    And now you can write:

    List<int> l = List<int>(); // this will call List<int>::List( List<int> const & )
    

    And yet, that will still not call the constructor you want. The reason is a little obscure, but when copy constructing an element of a template, the compiler will not use a templated constructor. In the code above, it will implicitly define a copy constructor by doing member-wise copy constructor of the methods and call that generated constructor. That means that in most occasions where you want to provide a templated constructor you want to also provide a non-templated copy constructor.

    To actually call that constructor you would have to provide a different type:

    List<int> l = List<double>();
    

    Since the types actually differ, the compiler cannot copy construct, will find that the provided templated constructor is the best overload candidate and call it.