代码之家  ›  专栏  ›  技术社区  ›  joke

如何在模板类中拆分模板友元函数的定义?

  •  4
  • joke  · 技术社区  · 16 年前

    下面的示例编译得很好,但我不知道如何将运算符的声明和定义分隔开来。

    每次我试图拆分定义时,friend都会引起麻烦,GCC抱怨操作员<<()定义必须采用一个参数。

    #include <iostream>
    template <typename T>
    class Test {
        public:
            Test(const T& value) : value_(value) {}
    
            template <typename STREAM>
            friend STREAM& operator<<(STREAM& os, const Test<T>& rhs) {
                os << rhs.value_;
                return os;
            }
        private:
            T value_;
    };
    
    int main() {
        std::cout << Test<int>(5) << std::endl;
    }
    

    operator<<()应该有一个空闲的first参数来处理不同类型的输出流(std::cout、std::wcout或boost::asio::ip::tcp::iostream)。第二个参数应绑定到周围类的专用版本。

    Test<int> x;
    some_other_class y;
    
    std::cout << x; // works
    boost::asio::ip::tcp::iostream << x; // works
    
    std::cout << y; // doesn't work
    boost::asio::ip::tcp::iostream << y; // works
    

    此外,使用非成员函数并不等同于拆分定义和声明,因为非成员函数不能访问类的私有属性。

    5 回复  |  直到 14 年前
        1
  •  4
  •   Nikolai Fetissov    16 年前

    最简单的可能是让所有这些模板操作员成为朋友:

    #include <iostream>
    template <typename T>
    class Test
    {
        public:
            Test(const T& value) : value_(value) {}
    
            template <typename STREAM, typename U>
            friend STREAM& operator<<(STREAM& os, const Test<U>& rhs);
    
        private:
            T value_;
    };
    
    template <typename STREAM, typename T>
    STREAM& operator<<( STREAM& os, const Test<T>& rhs )
    {
        os << rhs.value_;
        return os;
    }
    
        2
  •  1
  •   AProgrammer    16 年前

    我能做到的最接近的是

    #include <iostream>
    
    template <typename T>
    class Test;
    
    template <typename STREAM, typename T>
    STREAM& operator<<(STREAM& os, const Test<T>& rhs);
    
    template <typename T>
    class Test {
    public:
        Test(const T& value) : value_(value) {}
    
        template <typename STREAM, typename U>
        friend STREAM& operator<< (STREAM& os, const Test<U>& rhs);
    
    private:
        T value_;
    };
    
    template <typename STREAM, typename T>
    STREAM& operator<<(STREAM& os, const Test<T>& rhs) {
        os << rhs.value_;
        return os;
    }
    
    int main() {
        std::cout << Test<int>(5) << std::endl;
    }
    

    它声明所有运算符<<为友元,而不是仅由t参数化的运算符。问题在于,不可能部分专用化函数。有人想用

    template <typename STREAM>
    friend STREAM& operator<< <STREAM, T> (STREAM& os, const Test<T>& rhs);
    

    但这不是有效的语法。(好吧,部分专业化不能宣布为朋友)

        3
  •  1
  •   Luc Touraille    16 年前

    它不应该在类外定义吗?

    template <typename T>
    class Test 
    {  
        ...
        template <typename STREAM>
        friend STREAM& operator<<(STREAM& os, const Test<T>& rhs);
    };
    
    template <typename STREAM, typename T> 
    STREAM& operator<<(STREAM& os, const Test<T>& rhs) 
    {
        os << rhs.value_;
        return os;
    }
    
        4
  •  0
  •   David Rodríguez - dribeas    16 年前

    问题是,在您提供的代码中,friend是一个模板化函数,仅在第一个参数类型上参数化。也就是说,对于类模板的每个实例化类型t(调用它 mytype )声明一个自由模板函数:

    template <typename STREAM>
    STREAM& operator<<( STREAM& os, Test<mytype> const & x );
    

    重要的是 Test<mytype> 是 Test 带类型参数 MyType .

    如果您真的想声明一个友元函数,该函数在流类型和 试验 模板,您必须用两个参数声明一个朋友。

    另一方面,我建议您不要参数化 operator<< 在流类型上,同时在类大括号内定义它,因为它有一些优势(名称查找规则略有不同)。

        5
  •  0
  •   joke    16 年前

    对于每个实例化的类型t的类测试,将公开模板函数运算符<<(),该运算符可以在不同类型的流上操作。运算符<<()函数有一个自由的第一个参数,但有一个固定的第二个参数。

    例子:

    Test<int> x;
    some_other_class y;
    
    std::cout << x; // works
    boost::asio::ip::tcp::iostream << x; // works
    
    std::cout << y; // doesn't work
    boost::asio::ip::tcp::iostream << y; // works
    

    这就是测试课的工作方式。

    推荐文章