C ++模板构造函数不会编译

时间:2010-01-26 14:35:17

标签: c++ templates constructor typedef typename

为什么我不能用上面的构造函数实例化一个Foo类型的对象?

我有一个使用内部typedef的类Bar(作为“template typedefs”的解决方法),并打算在构造函数中使用它,如下所示(CASE 1)。 但是,我似乎没有得到它编译。这是合法的C ++吗? 案例2似乎表明问题与Bar中的typedef有关。

如何定义一个构造函数来接受具有Bar?

类型的对象的std :: vectors
#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;
}

4 个答案:

答案 0 :(得分:9)

根据C ++标准的第14.8.2.1段,当模板参数仅用于非推导的上下文时,不能推导出相应的模板参数:

  

如果 template-parameter 未在函数模板的任何函数参数中使用,或仅在非推导的上下文中使用,则其对应的 template-argument 无法从函数调用推断出,并且必须明确指定 template-argument

非受限上下文的定义,如§14.8.2.4中所述:

  

非弱势语境是:

     
      
  • 使用 qualified-id 指定的类型的嵌套名称说明符

  •   
  • 一个 template-id 的类型,其中一个或多个 template-arguments 是一个引用模板参数的表达式

  •   

Bar<T>::type中,Bar<T>嵌套名称说明符,因此是非推断的上下文,因此在调用构造函数时必须显式指定模板参数。 ..这是不可能的(即你不能写Foo f<int>(v))。

我认为编译器不能推导出模板参数,因为这至少是麻烦的,而且可能是不可能的:想象Bar是专门的:

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

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

现在,在使用std::vector<std::pair<int,int> >调用Foo的构造函数时,我有一个歧义:模板参数应该是int还是char?即使没有这种歧义,你也可以很容易地看到编译器在使用正确的typedef找到实例化之前必须实例化任何类型的Bar(好吧,我不太确定上面的语句是真正相关的,因为我经常找出编译器比我想象的要聪明得多: - )!)

答案 1 :(得分:4)

你的构造函数是:

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

模板参数T不能以这种方式从函数参数中推导出来。 (我认为这被称为“不可推翻的背景”,但我不确定。)

这根本行不通。你必须写

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

而是找到其他方法断言B的类型为typename Bar<T>::type

答案 2 :(得分:0)

我认为唯一的原因是你想要实例化相应类型的Bar而不是打印“hello”。

也许您可以尝试以相反的方向映射类型(您希望编译器能够执行的反向推导类型):

#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> >());
}

答案 3 :(得分:0)

这样的事情能为你效劳吗?

#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;
}

这在GCC 4.4.1上编译并运行。您可以将Traits专门用于构造函数允许的其他vector::value_type