【问题标题】:Specialization of template after instantiation?实例化后模板的专业化?
【发布时间】:2010-12-26 01:20:55
【问题描述】:

我的完整代码太长了,但这里有一个可以反映我问题本质的 sn-p:

class BPCFGParser {
  public:

  ...
  ...

  class Edge {
    ...
    ...
  };


  class ActiveEquivClass {
    ...
    ...
  };

  class PassiveEquivClass {
    ...
    ...
  };

  struct EqActiveEquivClass {
    ...
    ...
  };

  struct EqPassiveEquivClass {
    ...
    ...
  };



  unordered_map<ActiveEquivClass, Edge *, hash<ActiveEquivClass>, EqActiveEquivClass> discovered_active_edges;
  unordered_map<PassiveEquivClass, Edge *, hash<PassiveEquivClass>, EqPassiveEquivClass> discovered_passive_edges;

};

namespace std {


template <>
class hash<BPCFGParser::ActiveEquivClass>
{

    public:
        size_t operator()(const BPCFGParser::ActiveEquivClass & aec) const {

        }
};

template <>
class hash<BPCFGParser::PassiveEquivClass>
{

    public:
        size_t operator()(const BPCFGParser::PassiveEquivClass & pec) const {

        }
};

}

当我编译这段代码时,我得到以下错误:

In file included from BPCFGParser.cpp:3,
                 from experiments.cpp:2:
BPCFGParser.h:408: error: specialization of ‘std::hash<BPCFGParser::ActiveEquivClass>’     after instantiation
BPCFGParser.h:408: error: redefinition of ‘class                 std::hash<BPCFGParser::ActiveEquivClass>’
/usr/include/c++/4.3/tr1_impl/functional_hash.h:44: error: previous definition of     ‘class std::hash<BPCFGParser::ActiveEquivClass>’
BPCFGParser.h:445: error: specialization of     ‘std::hash<BPCFGParser::PassiveEquivClass>’ after instantiation
BPCFGParser.h:445: error: redefinition of ‘class std::hash<BPCFGParser::PassiveEquivClass>’
/usr/include/c++/4.3/tr1_impl/functional_hash.h:44: error: previous definition of     ‘class std::hash<BPCFGParser::PassiveEquivClass>’

现在我必须为这些类专门化 std::hash(因为标准的 std::hash 定义不包括用户定义的类型)。当我在类BPCFGParser 的定义之前移动这些模板特化时,尝试了各种不同的东西,我得到了各种错误,在某个地方(http://www.parashift.com/c++-faq-lite/misc-technical-issues.html)我读到:

每当您使用一个类作为模板参数时,该类的声明必须是完整的,而不是简单地向前声明。

所以我被困住了。我无法在BPCFGParser 定义之后专门化模板,我无法在BPCFGParser 定义之前专门化它们,我怎样才能让它工作?


您需要将专业化移动到 BPCFGParser 内部的内部类中。这样做符合这两个要求。

非常感谢您的回答:)

hash 类在命名空间std 中定义。它不允许我在非命名空间范围内专门化 hash 的模板。甚至如下:

template <>
  class std::hash<ActiveEquivClass> {
...

没用。然而,当我用namespace std {} 附上专业时,它给出了以下奇怪的错误:

In file included from BPCFGParser.cpp:3,
                 from experiments.cpp:2:
BPCFGParser.h:225: error: expected unqualified-id before ‘namespace’
experiments.cpp:7: error: expected `}' at end of input
BPCFGParser.h:222: error: expected unqualified-id at end of input

velocityreviews 给出的答案中,有人声称不能在类中定义命名空间。所以我还是卡住了。

【问题讨论】:

    标签: c++ templates instantiation specialization


    【解决方案1】:

    您需要将专业化移动到 BPCFGParser 内部的内部类中。这样做符合这两个要求

    1. 特化是在ActiveEquivClass的完整定义之后
    2. 在使用专业化之前

    例子:

    class BPCFGParser {
    
      class ActiveEquivClass {
        ...
      };
    
      template <>
      class hash<ActiveEquivClass> {
         public:
            size_t operator()(const BPCFGParser::ActiveEquivClass & aec) const {
            }
      };
      ...
      unordered_map<ActiveEquivClass, Edge *, hash<ActiveEquivClass>, EqActiveEquivClass> discovered_active_edges;
    
    };
    

    【讨论】:

    • 非常感谢您的回答,但它不起作用。解释见下文。
    【解决方案2】:

    尝试在 BPCFGParser 类声明之前移动 hash 模板特化代码。该错误意味着哈希是根据 /usr/include/c++/4.3/tr1_impl/functional_hash.h 中定义的 std::hash 扩展的;因此,在实例化之前不使用您的专业化。理想情况下,您的专业化代码应该在模板展开之前可供编译器使用。

    【讨论】:

      【解决方案3】:

      每当你使用一个类作为模板参数时,声明 该类必须是完整的,而不是简单地向前声明。

      这实际上不是真的。模板参数何时必须是完整类型的限制通常取决于模板内容;但只要模板不包含不完整类型的非法代码,实例化具有不完整类型的模板并不违法。

      解决问题的唯一方法是在类之前定义specialization,但在类之前定义实际的成员函数operator()。也就是说,

      template <>
      class std::hash<BPCFGParser::ActiveEquivClass>
      {
      public:
          size_t operator()(const BPCFGParser::ActiveEquivClass & aec) const;
      };
      
      // definition of BPCFGParser
      
      template<> std::size_t std::hash<BPCFGParser::ActiveEquivClass>::operator()(const BPCFGParser::ActiveEquivClass & aec) const {
      }
      

      这也意味着没有嵌套类,因为您不能转发声明嵌套类。

      【讨论】:

        【解决方案4】:

        我遇到了完全相同的问题,最后想出了一个用户定义的哈希函子解决方案,见下文:

        class Outer
        {
            // TC++PL, 4e, 19.4.1 : A friend must be previously declared in an enclosing scope or
            // defined in the non-class scope immediately enclosing the class that is declaring it to be a friend. 
            struct Hash_inner;
        
            class Inner
            {
                int i;
                friend struct Hash_inner;
            };
        
            struct Hash_inner
            {
                size_t operator()(const Inner& in) const
                { return std::hash<int>()(in.i); }
            };
        
            std::unordered_map<Inner, int, Hash_inner> um;
        };
        

        我仍然想知道是否有 std::hash 专业化方法。有人能猜出来吗?

        【讨论】:

          【解决方案5】:

          我知道这个问题已经很老了,但我刚刚遇到了同样的问题,我想我会报告我的发现。

          错误信息:

          In file included from BPCFGParser.cpp:3,
                       from experiments.cpp:2:
          BPCFGParser.h:408: error: specialization of ‘std::hash<BPCFGParser::ActiveEquivClass>’     after instantiation
          BPCFGParser.h:408: error: redefinition of ‘class                 std::hash<BPCFGParser::ActiveEquivClass>’
          /usr/include/c++/4.3/tr1_impl/functional_hash.h:44: error: previous definition of     ‘class std::hash<BPCFGParser::ActiveEquivClass>’
          BPCFGParser.h:445: error: specialization of     ‘std::hash<BPCFGParser::PassiveEquivClass>’ after instantiation
          BPCFGParser.h:445: error: redefinition of ‘class std::hash<BPCFGParser::PassiveEquivClass>’
          /usr/include/c++/4.3/tr1_impl/functional_hash.h:44: error: previous definition of     ‘class std::hash<BPCFGParser::PassiveEquivClass>’
          

          根本不是在抱怨你的类,而是说你不能专门化 std::hash 因为 std::hash 的通用模板或现有的专门化之一已经被使用 - 即有人使用过它介于定义它的点和您尝试对其进行专门化的点之间。

          this document 的“详细”部分中有一些文字描述了这一点:

          必须在第一次使用之前声明专业化,这会导致 隐式实例化

          就我而言,问题不在于我的专业化代码,而在于它的位置。一旦使用了 std::hash,就不能再专门化它了。

          我发现,如果我在包含 之后立即将我的专业化代码移动到,它可以正常工作。

          Puppy 将专业化声明与实现分开的建议允许您将声明移动到非常接近包含 的位置,实现可以在任何方便的地方稍后进行(在 BPCFGParser 在您的案例中完全定义之后)。

          【讨论】:

            【解决方案6】:

            在某个命名空间中定义成员类怎么样?

            #include <unordered_map>
            using std::unordered_map;
            using std::hash;
            
            namespace parser_detail
            {
                class ActiveEquivClass { };
                class PassiveEquivClass { };
            }
            
            namespace std {
                template <>
                class hash<parser_detail::ActiveEquivClass>
                {
                public:
                    size_t operator()(const parser_detail::ActiveEquivClass & aec) const { return 0; }
                };
            
                template <>
                class hash<parser_detail::PassiveEquivClass>
                {
                public:
                    size_t operator()(const parser_detail::PassiveEquivClass & aec) const { return 0; }
                };
            }
            
            class BPCFGParser {
            public:
                class Edge { };
            
                typedef parser_detail::ActiveEquivClass ActiveEquivClass;
                typedef parser_detail::PassiveEquivClass PassiveEquivClass;
            
                struct EqActiveEquivClass {
                    bool operator()(const ActiveEquivClass&, const ActiveEquivClass&) const { return false; }
                };
            
                struct EqPassiveEquivClass {
                    bool operator()(const PassiveEquivClass&, const PassiveEquivClass&) const { return false; }
                };
            
                unordered_map<ActiveEquivClass, Edge *, hash<ActiveEquivClass>, EqActiveEquivClass> discovered_active_edges;
                unordered_map<PassiveEquivClass, Edge *, hash<PassiveEquivClass>, EqPassiveEquivClass> discovered_passive_edges;
            };
            
            int main() { }
            

            【讨论】:

              猜你喜欢
              • 2020-07-18
              • 1970-01-01
              • 2011-10-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-12-28
              • 1970-01-01
              相关资源
              最近更新 更多