Как перегрузить бесплатную функцию для типа члена шаблона

У меня есть класс шаблона, который определяет некоторые типы членов. Это похоже на то, как std::map определяет value_type на основе собственных аргументов шаблона, но в моем случае тип более сложный, поэтому он определяется как вложенный класс.

Теперь для отладки я хотел бы определить operator<< для этого типа. Но компилятор говорит мне, что не может вывести параметры шаблона внешнего шаблона.

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

#include <iostream>

template <typename Value> class Outer;

template <typename Value>
std::ostream &operator<<(std::ostream &, const typename Outer<Value>::Inner &);

template <typename Value>
class Outer {
  public:
    struct Inner {
    Value x;
    };

    void PrintSomething(Value v) {
    // the real program does something useful with the inner class, of course
    Inner inner = { v };
    std::cout << "Inner = " << inner << std::endl; // <---- THIS SAYS IT CAN'T FIND operator<<
    };
};

template <typename Value>
std::ostream &operator<<(std::ostream &s, const typename Outer<Value>::Inner &v) {
    return s << v.x;
}

int main() {
    Outer<int> o;
    o.PrintSomething(42);
    return 0;
}

Это полный пример для воспроизведения проблемы. Компилятор (я пробовал 3 из них) говорит, что нет перегрузки operator<<, которая принимала бы второй аргумент типа Outer<int>::Inner. Когда я пробую то же самое с другой функцией, которая не имеет других перегрузок, вместо этого она говорит C2783: could not deduce template argument for 'identifier', gcc и clang продолжают говорить, что нет перегрузки, которая принимает второй аргумент Outer<int>::Inner).

Итак, есть ли способ определить operator<<, принимая Outer<Value>::Inner для любого Value в качестве правильного аргумента (чтобы его нельзя было определить как член)?

Примечание. Мне нужно, чтобы он компилировался в нескольких компиляторах, и некоторые из них не имеют каких-либо функций C++ 11, поэтому мне нужно, чтобы это был C++ 03.


person Jan Hudec    schedule 14.03.2013    source источник
comment
Сделайте его встроенным другом Inner.   -  person Xeo    schedule 14.03.2013
comment
@Xeo: Можешь дать ответ? Звучит как рабочая идея.   -  person Jan Hudec    schedule 14.03.2013
comment
Я не уверен, что ты сможешь это сделать. См. этот вопрос, который близок к вашему.   -  person Synxis    schedule 14.03.2013
comment
@Xeo: Да, можешь. Я пробовал это, и это работает. Спасибо.   -  person Jan Hudec    schedule 14.03.2013


Ответы (1)


У вас есть так называемый невыводимый контекст. Как вообще можно было вывести Value? Вы можете частично специализировать шаблоны классов, что делает практически невозможным для компилятора даже попытку и тестирование каждого возможного экземпляра (которых… ну, бесконечно).

Есть два обходных пути: убрать Inner из Outer или сделать operator<< встроенным другом. Последний - обычный путь, по которому идут люди.

template<class T>
struct Outer{
  struct Inner{
    T value;
    friend std::ostream& operator<<(std::ostream& os, Inner const& v){
      return os << v.value:
    }
  };
  // ...
};
person Xeo    schedule 14.03.2013
comment
На самом деле я перегружал не operator<< напрямую, а аналогичную функцию, специфичную для моих функций ведения журнала. А для нормальной работы встроенный друг скрывает другие перегрузки внутри класса, в том числе и внутри себя. Поэтому мне пришлось использовать некоторую косвенность, когда я хотел вызвать другую перегрузку для чего-то внутри. Но это небольшое препятствие. - person Jan Hudec; 14.03.2013