繁体   English   中英

operator<<(ostream&, X) for class X 嵌套在 class 模板中

[英]operator<<(ostream&, X) for class X nested in a class template

这个编译和工作就像它应该(非嵌套模板):

#include <iostream>

template<typename T> class Z;

template <typename T> 
std::ostream& operator<< (std::ostream& os, const Z<T>&) {
    return (os << "Z");
}

template<typename T> class Z {
    friend std::ostream& operator<< <> (std::ostream& os, const Z&);
};

int main () {
    Z<int> z;
    std::cout << z << std::endl;
}

这个不能编译(gcc 4.4 和 gcc 4.6,在 03 和 0x 模式下):

#include <iostream>

template<typename T> class Z;

template<typename T> 
std::ostream& operator<< (std::ostream& os, const typename Z<T>::ZZ&) {
    return (os << "ZZ!");
}

template <typename T> class Z {
  public:
    class ZZ {
        friend std::ostream& operator<< <> (std::ostream& os, const ZZ&);
    };
};


int main () {
    Z<int>::ZZ zz;
    std::cout << zz << std::endl;
}

错误消息如下所示:

error: template-id ‘operator<< <>’ for ‘std::ostream& operator<<(std::ostream&,
const Z<int>::ZZ&)’ does not match any template declaration
error: no match for ‘operator<<’ in ‘std::cout << zz’

在 0x 模式下,第二条错误信息不同,但含义相同。

有可能做我想做的事吗?

编辑显然,这里有一个非推断上下文的实例,它解释了错误消息。 然而,问题仍然存在:对于嵌套在 class 模板中的 class,我可以有一个工作operator<<吗?

这是函数的一般问题:

template <typename C>
void func(typename C::iterator i);

现在,如果我调用func(int*) ,我应该使用C的哪个值?

一般来说,你不能向后工作! 许多不同的C可能已经定义了一个内部类型iterator ,该迭代器恰好是某些参数集的int*

在你的情况下,你的情况有点复杂:

template <typename T>
void func(typename Z<T>::ZZ const&);

但基本上这是同一个问题, Z<T>是一个模板,而不是完整的 class,并且您要求为此模板的内部类型ZZ创建一个 function。

假设我这样做:

template <typename T>
struct Z { typedef T ZZ; };

template <typename T>
struct Z<T const> { typedef T ZZ; };

注意:典型的迭代器, value_type不是 const 限定的

那么,在调用func(int)时,我应该使用Z<int>还是Z<int const>

它是不可扣除的。

因此整个事情被称为不可演绎的上下文,标准禁止它,因为没有明智的答案。

经验法则:怀疑 function 参数中的typename

注意:如果另一个参数已经确定了类型,它们是可以的,例如typename C::iterator find(C&, typename C::const_reference); 因为一旦推导出C ,那么C::const_reference可以毫无问题地使用

除了友元声明与操作符模板不匹配的问题(或许可以修复为)

class ZZ {
    template<class U>
    friend std::ostream& operator<<(std::ostream& os, const ZZ&);
};

你也有一个“非推断上下文”的问题,这是马蒂厄所链接的。

在这个模板中

template<typename T> 
std::ostream& operator<< (std::ostream& os, const typename Z<T>::ZZ&) {
    return (os << "ZZ!");
}

编译器无法确定 T's you 参数将匹配的内容。 如果您专注于某些类型,可能会有多个匹配项

template<>
class Z<long>
{
public:
    typedef double   ZZ;
};

template<>
class Z<bool>
{
 public:
    typedef double   ZZ;
};

现在,如果我尝试打印doubleT可能是boollong

如果不检查所有可能的 T,编译器就无法确定这一点,而且它不必这样做。 它只是跳过您的运营商。

Matthieu 很好地解释了这个问题,但在这种情况下可以使用一个简单的解决方法。 您可以使用朋友声明在 class 中实现朋友 function :

#include <iostream>

template <typename T> class Z {
public:
  class ZZ {
    friend std::ostream& operator<< (std::ostream& os, const ZZ&) {
      return os << "ZZ!";
    }
  };
};


int main () {
  Z<int>::ZZ zz;
  std::cout << zz << std::endl;
}

一种不同的技术是提供一个外行的 class 模板,它像一个钩子一样工作。

template <typename T> class ZZZ { 
  T &getDerived() { return static_cast<T&>(*this); }
  T const &getDerived() const { return static_cast<T const&>(*this); }

protected:
  ~ZZZ() { }
};

template <typename T> class Z {
  public:
    class ZZ : public ZZZ<ZZ> {
    };
};

template<typename ZZ> 
std::ostream& operator<< (std::ostream& os, const ZZZ<ZZ> &zzz) {
    ZZ const& zz = zzz.getDerived();
    /* now you can use zz */
    return (os << "ZZ!");
}

不过最好使用朋友 function 定义。 但很高兴了解替代方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM