【问题标题】:templates problem ('typename' as not template function parameter)模板问题('typename' 不是模板函数参数)
【发布时间】:2010-12-08 16:46:14
【问题描述】:

其实我在用 intel 编译器编译一些库时遇到了问题。

这个库已经用 g++ 正确编译了。

问题是由模板引起的。 我想了解的是声明 **typename** 作为函数体内部的非模板函数参数和变量声明

示例:

void func(typename sometype){..
...
typename some_other_type;
..
}

编译这种代码会产生以下错误(intel),(gcc没有声明): 我有以下错误

../../../libs/log/src/attribute_set.cpp(415): error: no operator "!=" matches these operands
            operand types are: boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'> != boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'>
      while (begin != end)
                   ^
          detected during instantiation of "void boost::log_st::basic_attribute_set<CharT>::erase(boost::log_st::basic_attribute_set<CharT>::iter<'\000'>, boost::log_st::basic_attribute_set<CharT>::iter<'\000'>) [with CharT=wchar_t]" at line 438

../../../boost/log/attributes/attribute_set.hpp(115): error: no operator "!=" matches these operands
            operand types are: boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'> != boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'>
              if (it != m_pContainer->end())

我想了解的是函数体、参数声明中类型名的用法。

例如:

template< typename CharT >
struct basic_attribute_values_view< CharT >::implementation
{

public:
..
..
void adopt_nodes( **typename attribu**te_set_type::const_iterator& it, **typename attribut**e_set_type::const_iterator end)
    {
        for (; it != end; ++it)
            push_back(it->first, it->second.get());
    }

我在不同的文件中:

template< typename CharT >
class basic_attribute_set
{
    friend class basic_attribute_values_view< CharT >;

    //! Self type
    typedef basic_attribute_set< CharT > this_type;

public:
    //! Character type
    typedef CharT char_type;
    //! String type
    typedef std::basic_string< char_type > string_type;
    //! Key type
    typedef basic_slim_string< char_type > key_type;
    //! Mapped attribute type
    typedef shared_ptr< attribute > mapped_type;

    //! Value type
    typedef std::pair< const key_type, mapped_type > value_type;
    //! Allocator type
    typedef std::allocator< value_type > allocator_type;
    //! Reference type
    **typedef typename allocator_type::reference reference;**

【问题讨论】:

  • 这会更容易...如果我们有生成编译器错误的代码(指示有罪的行)。请注意,使用 '**' 表示语法在代码块中不起作用。
  • 我的示例中的所有迭代器 != 操作都会产生类似的错误,因此在我的第一个代码示例中将是 (it != end;) 行。

标签: c++ boost generic-programming


【解决方案1】:

typename 关键字的意义在于告诉编译器某些东西是类型名,在不明显的情况下。举个例子:

template<typename T>
void f()
{
    T::foo * x;
}

T::foo 是一个类型,意味着我们声明了一个指针,还是T::foo 是一个静态变量,我们正在做一个乘法?

由于编译器在读取模板时不知道 T 可能是什么,因此它不知道这两种情况中哪一种是正确的。

标准规定编译器应假定后一种情况,并且仅在 T::foo 前面带有 typename 关键字时将其解释为类型名,如下所示:

template<typename T>
void f()
{
    typename T::foo* x; //Definitely a pointer.
}

【讨论】:

    【解决方案2】:

    您需要将typename 用于所谓的“依赖类型”。这些类型依赖于模板参数并且在模板被实例化之前是未知的。最好用一个例子来解释:

    struct some_foo {
      typedef int bar;
    };
    
    template< typename Foo >
    struct baz {
      typedef Foo::bar barbar; // wrong, shouldn't compile
    
      barbar f(); // would be fine if barbar were a type
    
      // more stuff...
    };
    

    定义barbartypedef 需要typename 以便编译器能够检查模板是否存在明显的语法错误它被实例化为具体类型.原因是,当编译器第一次看到模板时(当它还没有用具体的模板参数实例化时),编译器不知道Foo::bar 是否是一个类型。据它所知,我可能打算用像这样的类型实例化 baz

    struct some_other_foo {
      static int bar;
    };
    

    在这种情况下Foo::bar 将引用一个对象,而不是一个类型,并且baz::bar 的定义将是句法废话。在不知道Foo::bar 是否指一种类型的情况下,编译器没有机会检查baz 中直接或间接使用barbar 的任何内容,即使是最愚蠢的拼写错误,直到baz 被实例化。使用正确的typenamebaz 看起来像这样:

    template< typename Foo >
    struct baz {
      typedef typename Foo::bar barbar;
    
      barbar f();
    
      // more stuff...
    };
    

    现在编译器至少知道Foo::bar 应该是一个类型的名称,这使得barbar 也成为一个类型名称。所以f() 的声明在语法上也是可以的。

    顺便说一句,模板而不是类型也有类似的问题:

    template< typename Foo >
    struct baz {
      Foo::bar<Foo> create_wrgl(); // wrong, shouldn't compile
    };
    

    当编译器“看到”Foo::bar 时,它不知道它是什么,所以bar&lt;Foo 也可以作为一个比较,让编译器对尾随的&gt; 感到困惑。在这里,您也需要向编译器提示 Foo::bar 应该是模板的名称:

    template< typename Foo >
    struct baz {
      Foo::template bar<Foo> create_wrgl();
    };
    

    注意:值得注意的是,Visual C++ 仍然没有实现正确的两阶段查找(实质上:在模板被实例化之前它不会真正检查模板)。因此,它经常接受遗漏typenametemplate 的错误代码。

    【讨论】:

    • +1 用于提及两阶段查找。因此建议:如果可能,请尝试使用至少 2 个不同的编译器来编译您的代码。
    【解决方案3】:

    根据您的代码:

    void func(typename sometype)
    {
        .....typename some_other_type;
        ..
    }
    

    如果上面的代码不是模板的一部分,那么它就不能用g++编译,除非是旧版本的g++。

    根据我的经验,FC9 或 GNU C/++ 版本 4.2x 会报告错误,它会抱怨:

    typename only can be used in template code
    

    而 FC8 或 GNU C/++ 4.1x 可能不会。

    请看

    http://code.google.com/p/effocore/source/browse/trunk/devel/effo/codebase/addons/inl/include/ringed_inl.h
    and 
    http://code.google.com/p/effocore/source/browse/trunk/devel/effo/codebase/addons/inl/include/cont_inl.h
    

    更多模板和类型名称示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-16
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      • 1970-01-01
      相关资源
      最近更新 更多