对于嵌套在类模板中的类X,运算符<(ostream&,X)

时间:2011-07-04 07:44:51

标签: c++ templates

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

#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模式下,第二条错误信息不同,但含义相同。

有可能做我想做的事吗?

编辑显然,此处有一个非推断上下文的实例,它解释了错误消息。但是,问题仍然存在:我可以为嵌套在类模板中的类设置operator<<吗?

4 个答案:

答案 0 :(得分:7)

这是功能的一般问题:

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>是一个模板,而不是一个完整的类,你要求为这个模板的内部类型ZZ创建一个函数。

假设我这样做:

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>

这是不可扣除的。

因此整个事情被称为不可推导的上下文,标准禁止它,因为没有合理的答案。

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

注意:如果另一个参数已经固定了类型,那么它们就可以了,例如typename C::iterator find(C&, typename C::const_reference);,因为一旦推断出C,那么C::const_reference可以毫无困难地使用

答案 1 :(得分:4)

除了友元声明与运营商模板(可能是可修复的)

不匹配的问题
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!");
}

编译器无法弄清楚你的参数匹配的是什么。如果您专注于某些类型

,可能会有几个匹配
template<>
class Z<long>
{
public:
    typedef double   ZZ;
};

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

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

如果没有检查所有可能的T,编译器就无法确定这一点,并且它不必这样做。它只是跳过你的操作员。

答案 2 :(得分:3)

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

#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;
}

答案 3 :(得分:2)

另一种技术是提供一个像钩子一样工作的外行类模板。

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!");
}

但最好使用友元函数定义。但了解替代品是件好事。