Специализация шаблона после создания экземпляра?

Мой полный код слишком длинный, но вот фрагмент, который отражает суть моей проблемы:

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, кто-то утверждает, что пространства имен не могут быть определены внутри классов. Так что я все еще застрял.


person Onur Cobanoglu    schedule 12.12.2009    source источник


Ответы (6)


Вам нужно переместить специализацию во внутренний класс внутри 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;

};
person JaredPar    schedule 12.12.2009
comment
Большое спасибо за ответ, но это не работает. См. Объяснение ниже. - person Onur Cobanoglu; 13.12.2009

Я знаю, что этот вопрос довольно старый, но я только что столкнулся с той же проблемой и подумал, что сообщу о своих выводах.

Сообщение об ошибке:

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, либо одна из существующих специализаций уже использовалась, то есть кто-то использовал ее между укажите, что он был определен, и точку, в которой вы пытаетесь его специализировать.

В разделе «подробно» этого документа есть текст, описывающий это:

Специализация должна быть объявлена ​​перед первым использованием, которое вызовет неявное создание экземпляра.

В моем случае проблема была не в моем коде специализации, проблема была в его положении. После того, как std :: hash был использован, вы не сможете его более специализировать.

Я обнаружил, что если я переместил свой код специализации сразу после включения ‹unordered_map›, он работал нормально.

Предложение Puppy отделить объявление специализации от реализации позволяет вам переместить объявления очень близко к включению ‹unordered_map›, реализация может появиться позже, когда это удобно (после того, как BPCFGParser полностью определен в вашем случае).

person sheltond    schedule 04.03.2016

Попробуйте переместить код специализации шаблона hash ‹> перед объявлением класса BPCFGParser. Ошибка означает, что хеш расширяется на основе std :: hash, определенного в /usr/include/c++/4.3/tr1_impl/functional_hash.h; Таким образом, ваша специализация не используется до создания экземпляра. В идеале код вашей специализации должен быть доступен для компилятора до того, как шаблон будет расширен.

person Sudarsun Santhiappan    schedule 13.04.2010

Всякий раз, когда вы используете класс в качестве параметра шаблона, объявление этого класса должно быть полным, а не просто объявленным вперед.

На самом деле это не так. Ограничения того, когда параметры шаблона должны быть полными типами, обычно зависят от содержимого шаблона; но создание экземпляра шаблона с неполным типом не является незаконным, если шаблон не содержит кода, недопустимого для неполного типа.

Единственный способ решить вашу проблему - определить специализацию перед классом, но определить фактическую функцию-член 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 {
}

Это также означает отсутствие вложенных классов, поскольку вы не можете пересылать объявление вложенного класса.

person Puppy    schedule 09.05.2012

У меня точно такая же проблема, и я, наконец, придумал обходное решение, определяемое пользователем хеш-функтора, см. Ниже:

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. Кто-нибудь может разобраться?

person wpzdm    schedule 18.09.2013

Как насчет определения классов-членов в определенном пространстве имен?

#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() { }
person jerry_fuyi    schedule 24.04.2020