我正在为学校作业编写一个小型日历库的最后阶段,但遇到了一个意料之外的,非常令人困惑的问题。介绍模板时,我的赋值运算符不会被覆盖!

因此结构如下:我有一个抽象类Date,其中大多数纯虚拟函数(包括赋值运算符),然后有两个子类GregorianJulian实现了所有功能。最后,我为Calendar类编写了一个模板,其中包含今天的日期(以GregorianJulian对象的形式)和其他与该特定问题无关的东西。

问题是,当尝试设置此成员today时,出现长链接错误:



告诉我在operator=类中找不到函数Date(显然是因为它是纯虚拟的)。为什么不使用任何被覆盖的?
告诉我Gregorian::operator=试图调用Date::operator=吗?

这是出错的简化代码:

namespace cal_lib {
    template <typename T>
    class Calendar {
    public:
        Calendar() {
            today = T(); // this yields the error
        }
    private:
        T today;
    };
 }

这是Gregorian.cpp的片段:
namespace cal_lib {
    class Gregorian : public Date {
    public:
        Gregorian();
        virtual Gregorian& operator=(const Date& date);
        virtual Date& add_year(int n = 1);
        virtual Date& add_month(int n = 1);
    };

    // here I'm using Date's constructor to get the current date
    Gregorian::Gregorian() {}

    Gregorian& Gregorian::operator=(const Date& date) {
        if (this != &date) {
            // these member variables are specified as
            // protected in Date
            m_year = 1858;
            m_month = 11;
            m_day = 17;

            add_year(date.mod_julian_day()/365);
            add_month((date.mod_julian_day() - mod_julian_day())/31);
            operator+=(date.mod_julian_day() - mod_julian_day());
        }
    }
}

Date的(默认)构造函数只是将m_yearm_monthm_day的值设置为今天的日期:
Date::Date() {
    time_t t;
    time(&t);
    struct tm* now = gmtime(&t);
    m_year = now->tm_year + 1900;
    m_month = now->tm_mon + 1;
    m_day = now->tm_mday;
}

值得一提的是,它可以很好地工作:
Gregorian g;
Julian j;
g = j; // no problems here

Date* gp = new Gregorian();
Date* jp = new Julian();
*gp = *jp; // no problems here either

这是Calendar类的实例化方式:
using namespace cal_lib;

Calendar<Gregorian> cal;

我在这里有一些很明显的错误吗?

最佳答案

如果您仔细阅读错误消息,您会注意到两件事,则编译器正在尝试查找以下内容的定义:

Date& operator=(const Date&)

从以下定义中需要符号:
Gregorian& operator=(const Gregorian&)

*那么那个运算符甚至如何出现在您的程序中? *

复制构造函数和赋值运算符是特殊的,它们将始终在程序中声明。您提供一个声明,否则编译器将为您完成声明。您已经提供了Gregorian& Gregorian::operator=(const Date&),但是并没有从程序中删除Gregorian& Gregorian::operator=(const Gregorian&)

当您尝试将一个Gregorian对象分配给另一个对象时,编译器将找到您的两个重载对象和隐式定义的对象,而重载解析将发现隐式声明的赋值是更好的匹配。这将以类似于以下方式触发赋值运算符的定义:
T& operator=( T const & o ) {
   Base::operator=(o);           // for all bases
   member=o.member;              // for all members
}

您可以采取不同的措施来解决此问题。最简单的方法可能是在程序中定义Date Date::operator=(const Date&)(将其保留为纯虚函数,但还要提供定义)。这样,当编译器遇到两个相同派生类型的对象时,它可以发挥作用,一切都会正常进行。您也可以通过强制派生类型中的调度,将其用作分解基础成员副本的实现的方法。

另一个需要付出更多努力的选择是为所有派生类型实际声明和定义赋值运算符。这里还有更多代码要编写,并且处理Date成员的代码将需要重复,如果您修改基类,则需要更新所有派生的赋值运算符...我将不赘述小路。

最后,解决了编译器错误之后,请考虑对通用Date(实际上可能是Gregorian日期)​​执行赋值运算符是否有意义。考虑以下情况会发生什么:
Gregorian d1 = ...; // some date
Gregorian d2 = d1;  // same date
Date &d = d2;
d1 = d2;            // What date does 'd1' represent??

请注意,如果您提供Date::operator=的定义并让编译器为您生成Gregorian::operator=(Gregorian const&),则本示例中的问题将消失。

09-05 06:34