2016-11-22 2 views
2

Вот цель (код упрощен, конечно):Класс, который содержит набор себя с пользовательской компаратором - циклическая ссылка

#include <set> 

struct Node 
{ 
    int Value; 
    std::set<Node*, CompareNodes> Children; 
}; 

struct CompareNodes 
{ 
    bool operator()(const Node* l, const Node* r) 
    { 
     return l->Value < r->Value; 
    } 
}; 

Но это не компилируется; тип CompareNodes неизвестен, когда он встречается внутри Node. Я мог бы изменить порядок, но тогда у меня есть противоположная проблема: Node будет неизвестен, когда он встречается внутри CompareNodes. Переадресация декларации не помогает в любом случае, потому что каждый тип использует другое так, как требуется его полное определение. Уродливое обходное решение таково:

#include <set> 

template<typename T> 
struct CompareNodes 
{ 
    bool operator()(const T* l, const T* r) 
    { 
     return l->Value < r->Value; 
    } 
}; 

struct Node 
{ 
    int Value; 
    std::set<Node*, CompareNodes<Node>> Children; 
}; 

Есть ли лучший способ? Бонусные очки, если ответ будет работать, даже если Node является внутренним классом.

+0

Вы все равно не можете делать стандартные контейнеры библиотеки неполных типов, по крайней мере, не переносимыми. – juanchopanza

+0

На простом английском языке у вас не может быть класса, который содержит набор сам по себе. – juanchopanza

+0

@juanchopanza Hm. Он может быть не переносимым, но мой компилятор позволяет это. Он все еще считается неполным, если я изменю его на 'std :: set '? – dlf

ответ

3

Вы можете использовать вложенный класс в качестве компаратора:

#include <set> 

struct Node 
{ 
    struct CompareNodes 
    { 
     bool operator()(const Node& l, const Node& r) 
     { 
     return l.Value < r.Value; 
     } 
    }; 

    int Value; 
    std::set<Node, CompareNodes> Children; 
}; 
2

вперед декларация не поможет в любом случае

Не совсем верно. Форвард декларация будет работать нормально:

#include <set> 

struct Node; 

struct CompareNodes 
{ 
    bool operator()(const Node& l, const Node& r); 
}; 

struct Node 
{ 
    int Value; 
    std::set<Node, CompareNodes> Children; 
}; 

bool CompareNodes::operator()(const Node& l, const Node& r) 
{ 
    return l.Value < r.Value; 
} 

Если функция член operator() должна быть объявлена ​​в заголовочном файле, то необходимо будет придерживаться в inline ключевое слово к нему.

+0

Yikes; это должно было быть очевидным. Я слишком долго не был в C++ ... – dlf

+3

Я верю, что 'sts :: set ' требует, чтобы 'Node' был полным типом? Или это изменилось недавно? – Yakk

1

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

#include <set> 

struct CompareNodes; 

template <class Cmp = CompareNodes> 
struct Node 
{ 
    int Value; 
    std::set<Node*, Cmp> Children; 
}; 

struct CompareNodes 
{ 
    bool operator()(const Node<>* l, const Node<>* r) 
    { 
     return l->Value < r->Value; 
    } 
}; 

int main() { 
    Node<> n; 
} 
Смежные вопросы