【问题标题】:Overloading friend iostream operators in a varadic template [duplicate]在可变参数模板中重载朋友ostream运算符[重复]
【发布时间】:2018-04-06 07:38:30
【问题描述】:

我正在尝试将 iostream 运算符实现为可变参数类模板的友元函数。

#include <utility>
#include <iostream>

template<typename... Args>
class StudentInformation {
public:
    //friend class Student;
    using Members = std::tuple<Args...>;
    Members members;

    const size_t numArgs{ sizeof...(Args) };

    StudentInformation( Args&&... args ) : members{ std::make_tuple<Args...>( std::move( args )... ) } {}


    const StudentInformation<Args...>& operator() ( Args... args ) {
        members = std::make_tuple<Args>( std::forward<Args>( args )... );
        return *this;
    }

    const StudentInformation<Args...> operator() ( Args... args ) const {
        members = std::make_tuple<Args>( std::forward<Args>( args )... );
        return *this;
    }


    friend std::ostream& operator<< ( std::ostream& out, const StudentInformation& c );
    friend std::istream& operator>> ( std::istream& in, StudentInformation& c );
};

template<typename... T>
std::ostream& operator<<( std::ostream& out, const StudentInformation<T...>& c ) {
    const size_t numArgs = c.numArgs;
    for( size_t idx = 0; idx < numArgs; idx++ )
        out << std::get<idx>( c.members ) << " ";
    return out;
}

template<typename... T>
std::istream& operator>>( std::istream& in, StudentInformation<T...>& c ) {
    const size_t numArgs = c.numArgs;
    for( size_t idx = 0; idx < numArgs; idx++ )
        in >> std::get<idx>( c.members );
    return in;
}

由于某种原因,我仍然收到链接器错误。即使在标头中定义了重载,我仍然会收到链接器错误,就好像它们是在 cpp 文件中定义的一样。有什么想法吗?


即使我尝试这种方法:首先声明类的原型,然后声明重载运算符的原型,然后声明类,然后定义友元重载运算符:

#include <utility>
#include <iostream>

template<typename... Args>
class StudentInfo;

template<typename... Args>
std::ostream& operator<<( std::ostream& out, const StudentInfo<Args...>& c );

template<typename... Args>
std::istream& operator>>( std::istream& in, StudentInfo<Args...>& c );

template<typename... Args>
class StudentInfo {
public:
    //friend class Student;
    using Members = std::tuple<Args...>;
    Members members;

    const size_t numArgs{ sizeof...(Args) };

    StudentInfo( Args&&... args ) : members{ std::make_tuple<Args...>( std::move( args )... ) } {}


    const StudentInfo<Args...>& operator() ( Args... args ) {
        members = std::make_tuple<Args>( std::forward<Args>( args )... );
        return *this;
    }

    const StudentInfo<Args...> operator() ( Args... args ) const {
        members = std::make_tuple<Args>( std::forward<Args>( args )... );
        return *this;
    }


    friend std::ostream& operator<< ( std::ostream& out, const StudentInfo& c );
    friend std::istream& operator>> ( std::istream& in, StudentInfo& c );
};

template<typename... T>
std::ostream& operator<<( std::ostream& out, const StudentInfo<T...>& c ) {
    const size_t numArgs = c.numArgs;
    for( size_t idx = 0; idx < numArgs; idx++ )
        out << std::get<idx>( c.members ) << " ";
    return out;
}

template<typename... T>
std::istream& operator>>( std::istream& in, StudentInfo<T...>& c ) {
    const size_t numArgs = c.numArgs;
    for( size_t idx = 0; idx < numArgs; idx++ )
        in >> std::get<idx>( c.members );
    return in;
}

我仍然收到链接器错误。我可以让这些成为课堂的一部分;但我不想要这种行为。我希望它们在课堂之外定义。按照目前的情况;现在类中的所有内容都是公开的,仅用于测试,但一旦该类按预期工作;其中的所有内容都是私有的,因为这个类将成为另一个类的朋友;拥有类将可以访问其所有私有信息。我不知道如何绕过或解决此链接器错误问题。

1>main.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class StudentInfo<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::vector<int,class std::allocator<int> > > const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$StudentInfo@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@HV12@V?$vector@HV?$allocator@H@std@@@2@@@@Z) referenced in function _main

【问题讨论】:

  • 我在几天前发布了此内容,但没有机会查看我发布的内容。当我们要去探望家人周末时,我很着急。我说过我在 cpp 文件中定义了运算符,但实际上我将它们放在包含在头文件底部的内联文件中。
  • 我对我的原始问题进行了一些编辑,以更具体地说明为什么它不是重复的,希望能解决我遇到的链接器错误。

标签: operator-overloading variadic-templates iostream c++17 friend


【解决方案1】:

这是经典。

当你编译main()(比如在“main.cpp”中)时,你有

std::cout << studentA << '\n';

其中studentAStudentInformation&lt;std::string, std::string, int, std::string, std::vector&lt;int&gt;&gt;

因此编译器准备了一个中间文件,该文件要求(调用)operator&lt;&lt;() for StudentInformation&lt;std::string, std::string, int, std::string, std::vector&lt;int&gt;&gt;

operator&lt;&lt;() 应该通过编译另一个 cpp 文件来实现,比如“operators.cpp”。

但是编译“operator.cpp”时,编译器不知道StudentInformation&lt;std::string, std::string, int, std::string, std::vector&lt;int&gt;&gt; 需要一个operator&lt;&lt;()。所以没有实现。

链接器接收编译“main.cpp”和“operators.cpp”产生的中间文件;在第一个中找到对operator&lt;&lt;()StudentInformation&lt;std::string, std::string, int, std::string, std::vector&lt;int&gt;&gt; 的调用,但没有找到实现:链接器错误。

这是因为最好在头文件中定义模板类(和相应的模板友元运算符),而不是在 cpp 文件中。

奖金错误。

此代码在operator&lt;&lt;()

for( size_t idx = 0; idx < numArgs; idx++ )
    out << std::get<idx>( c.members) << " ";

这个代码在operator&gt;&gt;()

for( size_t idx = 0; idx < numArgs; idx++ )
    in >> std::get<idx>( c.members);

显然是错误的,因为std::get() 对于模板参数需要一个编译时知道的值,而idx 显然在编译时不知道。

【讨论】:

  • 感谢您的洞察力。我想我以前遇到过这个;但似乎在我的项目和这里都找不到解决方案。
  • 我几天前发布了代码。我很着急,因为我们这个周末要去探望家人,所以我没有机会查看我发布的问题。我说过我已经在一个 cpp 文件中实现了操作符,但这实际上是错误的。我将它们放在*.inl 文件中,但仍然遇到相同的链接器错误。 *.inl 文件包含在类模板声明之后的头文件底部。有什么想法吗?
  • 对我来说并不完全清楚。我建议准备一个完整的示例(尽可能少)来重现错误。对于“完整”,我的意思是 all 文件及其名称,全部包括,main(),用于编译的命令行和用于链接的命令行。此问题已作为重复问题关闭,因此我建议打开另一个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-17
  • 1970-01-01
相关资源
最近更新 更多