开发者

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 co开发者_运维百科nstructor 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;
}


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 :-)!)


Your constructor is:

template <typename T> 
explicit 
Foo( const std::vector<typename Bar<T>::type>& data )

The template argument T cannot be deduced from the function argument this way. (I think this is called a "non-deducible context", but I'm not sure.)

This simply won't work. You'll have to write

template <typename B> 
explicit 
Foo( const std::vector<B>& data )

instead and find other ways to assert that B is of type typename Bar<T>::type.


I think the only reason for that is that you want to instantiate a Bar of corresponding type instead of printing "hello".

Perhaps you could try mapping the types in the reversed direction (the kind of reversed deduction that you are hoping the compiler would be able to perform):

#include <utility>
#include <vector>

template <class T>
struct BarType;

template <class T>
struct BarType<std::pair<T, T> >
{
    typedef T type;
};

template <class T>
struct Bar {};

struct Foo
{
    template <class T>
    Foo(const std::vector<T>& )
    {
        Bar<typename BarType<T>::type> bar;
        //...
    }
};

int main()
{
    Foo(std::vector<std::pair<int, int> >());
}


Could something like this work for you?

#include <vector>
#include <iostream>
#include <utility>
#include <boost/static_assert.hpp>

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

template <typename T>
struct Traits
{
    enum {allowed = false};
};

template <typename T>
struct Traits<std::pair<T, T> >
{
    enum {allowed = true};
    typedef Bar<T> BarType;
};

struct Foo
{
    Foo() {}

    template <typename T> explicit Foo( const std::vector<T>& data )
    {
        BOOST_STATIC_ASSERT(Traits<T>::allowed);
        typedef typename Traits<T>::BarType BarType;
        std::cout << BarType::val << std::endl;
    }
};

int main()
{
    std::vector<Bar<int>::type> v;
    std::vector<float> v2;

    Foo f( v );
//    Foo f2( v2 ); // Compile error
    return 0;
}

This compiles and runs on GCC 4.4.1. You can specialize Traits for other vector::value_type's that are allowed by your constructor.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜