如何定义在两个类之外的模板类内部的非模板类中声明的友元函数?

时间:2017-09-14 05:59:22

标签: c++ c++11 templates inner-classes friend

我发现"如何在声明之外定义模板类的朋友模板函数" (SO / cppreference),但如果我们在混合中添加另一个内部非模板类,该怎么做?

即。如何(从外部)定义operator<<class Internal中声明的#include <iostream> template <typename T> class External { public: explicit External(T initial) : value{initial} {} class Internal { public: Internal(const External& e) : internal_value{e.value} {} private: friend std::ostream& operator<<(std::ostream& os, const Internal& i); // ^^^ this one /* body { return os << i.internal_value; } */ T internal_value; }; friend std::ostream& operator<<(std::ostream& os, const External& e) { return os << Internal{e}; } private: T value; }; int main() { std::cout << External<int>{5}; } 来自以下示例:

Source bufferSource = Okio.buffer(Okio.source(inputStream));

1 个答案:

答案 0 :(得分:6)

这是问题所在。尽管External是模板而Internal是依赖类型。朋友的功能本身并不是模板化的。虽然看似奇怪,但它并不依赖于模板参数。

当您将其内联定义时,模板的每个特化也会创建相关的友元函数。但是,如果它不是内联的,则需要为每个专门化明确提供运算符的定义。并且您无法使用模板执行此操作,因为该功能不是模板。

因此,如果您在模板声明后添加它:

std::ostream& operator<<(std::ostream& os, External<int>::Internal const& i)
{
    return os << i.internal_value;
}

它将构建。正如您所看到的,函数中只使用了具体的类型。

显然,这不是一个很好的解决方案。任何明智的人都希望External的特化也能生成朋友定义。以可维护的方式实现这一目标的方法是保持运算符定义内联,但不是在那里进行工作,而是委托成员函数( 依赖于模板参数):

class Internal {
public:
    Internal(const External& e) : internal_value{e.value} {}

private:
    std::ostream& print(std::ostream&) const;
    friend std::ostream& operator<<(std::ostream& os, Internal const& i)
    {
        return i.print(os); // Short and sweet on account of being inline
    }

    T internal_value;
};

//....

template<typename T>
std::ostream& External<T>::Internal::print(std::ostream& os) const {
   // Provided outside of the class definition, can be as verbose as you like
   return os << internal_value;
}