2013-04-18 3 views
2

Я ожидал, что этот код будет работать, но он не скомпилируется с GCC. Он компилируется, если вы поднимаете внутренний класс.C++ STL не находит компаратор для вложенного класса

#include <algorithm> 

template <typename T> 
struct Outer 
{ 
    struct Inner 
    { 
    int x; 
    }; 
    Inner vec[3]; 
}; 

template <typename T> 
bool operator <(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs) 
{ 
    return lhs.x < rhs.x; 
} 

int main() 
{ 
    Outer<int> out; 
    Outer<int>::Inner in; 
    std::lower_bound(out.vec, out.vec + 3, in); 
} 

GCC 4.4 это сказать:

... 
bits/stl_algo.h:2442: error: no match for ‘operator<’ in ‘* __middle < __val’ 

GCC 4.7 печатает намного больше вещей, в том числе выше, заканчивающийся с этим:

... 
bits/stl_algobase.h:957:4: note: couldn't deduce template parameter ‘T’ 

Я готов поверить это не хорошо сформированный C++, но почему бы и нет?

ответ

2

Проблема, о которой вы говорили, заключается в том, что компилятор не смог вывести параметр шаблона T. Это потому, что имяТип Outer :: Inner является nondeduced контекста контекстом Т.

Когда параметр шаблона используются только в nondeduced контекста, это не учитывается для аргументов шаблона вывода. Подробности приведены в разделе 14.8.2.4 Стандарта C++ (2003).

Почему? Если вы специализируетесь Outer как:

template <> 
struct Outer<int> 
{ 
    struct Inner 
    { 
     double y; 
    }; 
    Inner vec[3]; 
}; 

Там нет никакого способа для компилятора вывести, если Outer :: Inner следует ссылаться на это определение или предыдущий.

Теперь на решения. Существует несколько возможных разрешений:

  1. Переместить оператора < внутри Внутреннего и сделать его функцией друга.
  2. Определить оператора < внутри Внутренний как M M..
  3. Предлагается Johannes Schaub - litb: Производит внутреннее из базы CRTP, параметризованное внутренним. Затем введите тип параметра CRTP-класс и передайте ссылку параметра на производный внутренний класс, тип которого задается аргументом шаблона, который вы выводите.

Я опробовал CRTP подход, поскольку это звучит так круто !:

template <typename Inner> 
struct Base 
{ 
}; 

template <typename T> 
struct Outer 
{ 
    struct Inner : Base<Inner> 
    { 
    T x; 
    }; 
    Inner vec[3]; 
}; 

template <typename T> 
bool operator< (const Base<T>& lhs, const Base<T>& rhs) 
{ 
    return static_cast<const T&>(lhs).x < static_cast<const T&>(rhs).x; 
} 

Похожие ответы: 1, 2, 3

+0

Мне нравится ваш ответ, даже если мне не нравится, что это означает для C++. Спасибо, что нашли время - вы наверняка заработали щедрость! –

2

Если перегрузить конкретный operator< для int проблема исчезнет:

bool operator<(const typename Outer<int>::Inner& lhs, 
       const typename Outer<int>::Inner& rhs) 
{ 
    return lhs.x < rhs.x; 
} 

 

Чем проще решение является определение operator< внутри Inner:

template<typename T> 
struct Outer 
{ 

    struct Inner 
    { 
     int x; 

     bool operator<(const Inner& obj) const 
     { 
      return x < obj.x; 
     } 

    }; 
    Inner vec[3]; 
}; 

Кроме того, это просто быстрое решение. И мой ответ не почему компилятор не может найти operator< во вложенной ситуации в режиме шаблона.

+0

О, доброта, код, который я опубликовал, был слишком упрощен, чтобы показать всю проблему. Позвольте мне попробовать еще раз и убедиться, что здесь есть настоящая проблема, а не только моя неспособность сделать правильный тест! Благодарю. –

+0

ОК, я обновил код в вопросе, чтобы лучше отразить проблему. Можете ли вы еще раз посмотреть? –

+0

@JohnZwinck: Я только что обновил свой ответ на основе вашего нового редактирования, чтобы сделать его актуальным. – deepmax

2

Вот еще одно обходное решение. Почему вы не используете пользовательский сопоставитель?

template <typename T> 
struct Comparer 
{ 
    bool operator()(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs) 
    { 
     return lhs.x < rhs.x; 
    } 
}; 

int main() 
{ 
    Outer<int> out; 
    Outer<int>::Inner in; 
    std::lower_bound(out.vec, out.vec + 3, in, Comparer<int>()); 
} 

Надеюсь, это сработает для вас.

+0

Я могу использовать это как обходной путь; Я все равно хотел бы знать, почему код в моем вопросе не «просто работает». –

1

ответ Mar0ux является довольно хорошо. Вы можете найти дополнительную информацию здесь:

Stephan T. Lavavej: Core C++, 2 of n

Вы должны смотреть весь видеоряд - она ​​содержит много полезной информации, но вы можете начать с минуты 34 или так, чтобы получить ответ на свой вопрос. Стивен упоминает одно основное правило, которое следует учитывать:

:: - кирпичная стена для вывода аргумента шаблона, то есть аргумент шаблона T с левой стороны не может быть выведен.

+0

Мне тоже нравится этот ответ - особенно «' :: 'является кирпичной стеной для вывода аргументов шаблона», что приятно и понятно. –