Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ templated constructor won't compile

How come I can't instantiate an object of type Foo with above constructor?

I have a class Bar that uses an internal typedef (as a workaround for "template typedefs") and intend to use it in a constructor as below (CASE 1). However, I don't seem to get it to compile. Is this legal C++? CASE 2 seems to suggest the problem is related to the typedef in Bar.

How can I define a constructor that will accept std::vectors of objects with the type in Bar?

#include <vector>
#include <iostream>
#include <utility>

template <typename T>
struct Bar
{
    typedef std::pair<T, T> type; // or anything else that uses T
};

struct Foo
{
    Foo() {}

    // CASE 1: doesn't compile
    template <typename T> explicit Foo( const std::vector<typename Bar<T>::type>& data )
    {
        std::cout << "Hello\n";
    }

    //// CASE 2: compiles, but it's not what I want
    //template <typename T> explicit Foo( const std::vector<Bar<T> >& data )
    //{
    //  std::cout << "Hello\n";
    //}
};

int main()
{
    std::vector<Bar<int>::type> v; // for CASE 1
    //std::vector<Bar<int> > v; // for CASE 2

    Foo f( v );
    return 0;
}
like image 766
kmhofmann Avatar asked Jan 26 '10 14:01

kmhofmann


1 Answers

According to paragraph 14.8.2.1 of the C++ standard, when a template parameter is used only in a non-deduced context, the corresponding template argument cannot be deduced:

If a template-parameter is not used in any of the function parameters of a function template, or is used only in a non-deduced context, its corresponding template-argument cannot be deduced from a function call and the template-argument must be explicitly specified.

The definition of nondeduced contexts, as stated in §14.8.2.4:

The nondeduced contexts are:

  • the nested-name-specifier of a type that was specified using a qualified-id.

  • A type that is a template-id in wich one or more of the template-arguments is an expression that references a template-parameter.

In Bar<T>::type, Bar<T> is a nested-name-specifier and hence a non-deduced context, so you must explicitly specify the template argument when calling the constructor...which is not possible (i.e. you cannot write Foo f<int>(v)).

I suppose the compiler cannot deduce the template argument because that would be at least cumbersome, and more probably impossible: imagine Bar is specialized:

template<typename T>
struct Bar
{
    typedef std::pair<T,T> type;
};

template<>
struct Bar<char>
{
    typedef std::pair<int,int> type;
};

Now I have an ambiguity when calling Foo's constructor with std::vector<std::pair<int,int> >: should the template argument be int or char? And even if there was no such ambiguity, you can easily see that the compiler would have to instantiate Bar with potentially any type before finding the instantiation with the correct typedef (well, I'm not so sure above statements are truly relevant since I often find out compilers to be much more smarter than I thought :-)!)

like image 65
Luc Touraille Avatar answered Oct 19 '22 00:10

Luc Touraille