到处都有差异的虚函数

Virtual functions with differences here and there

考虑这个输出:

Current time: 6:30 pm
Current time: 18:30
Current time: evening.
Current time: evening (for many it is dinner time, but many eat dinner later).

注意最后两个有句点,而前两个没有。我使用以下代码中的 System::displayCurrentTime 成员函数获得了所需的输出:

#include <iostream>
#include <string>
#include <memory>

class TimeDisplay {
    public:
        virtual std::string tell() const = 0;
        virtual std::string tellMaybeWithPeriod() const = 0;
};

class ClockDisplay12Hours : public TimeDisplay {  // #1
    std::string tell() const override {return "6:30 pm";}
    std::string tellMaybeWithPeriod() const override {return tell();}
};

class ClockDisplay24Hours : public TimeDisplay {  // #2
    std::string tell() const override {return "18:30";}
    std::string tellMaybeWithPeriod() const override {return tell();}
};

class DescriptiveTimeDisplay : public TimeDisplay {  // #3
    std::string tell() const override {return "evening";}
    std::string tellMaybeWithPeriod() const override {return tell() + ".";}
};

class CrazyDescriptiveTimeDisplay : public TimeDisplay {  // #4
    std::string tell() const override {return "evening (for many it is dinner time, but many eat dinner later)";}
    std::string tellMaybeWithPeriod() const override {return tell() + ".";}
};

struct System {
    static std::shared_ptr<TimeDisplay> timeDisplay;
    static std::string timeAsString() {return timeDisplay->tell();}
    static std::string timeAsStringMaybeWithPeriod() {return timeDisplay->tellMaybeWithPeriod();}
    // #3 and #4 will have a period, the others will not.
    static void displayCurrentTime (std::shared_ptr<TimeDisplay> t) {
        timeDisplay = t;
        std::cout << "Current time: " << System::timeAsStringMaybeWithPeriod() << '\n';
    }
    static void foo (std::shared_ptr<TimeDisplay>) {}  // #1 and #3 will have a period, the others will not.
    static void bar (std::shared_ptr<TimeDisplay>) {}  // #1, #2, and #4 will have a period, the others will not.
    static void baz (std::shared_ptr<TimeDisplay>) {}  // #2 will have a period, the others will not
};
std::shared_ptr<TimeDisplay> System::timeDisplay;

int main() {
    const std::shared_ptr<TimeDisplay> clocks[] = {std::make_shared<ClockDisplay12Hours>(), std::make_shared<ClockDisplay24Hours>(),
        std::make_shared<DescriptiveTimeDisplay>(), std::make_shared<CrazyDescriptiveTimeDisplay>()};
    for (std::shared_ptr<TimeDisplay> t : clocks)
        System::displayCurrentTime(t);
}

这不是非常混乱,但请注意下一个要实现的函数 foobarbaz 需要具有不同派生 类 的周期 TimeDisplay,而且实际上有超过 4 个这样的派生 类,还有 3 个以上的新成员函数需要处理。有没有比为 foobarbaz 等中的每一个写出新的虚函数更干净更优雅的方法来处理这些即将到来的成员函数...那个时期,哪个不会?以某种方式使用模板(例如,重命名派生的 类 Derived<0>Derived<1> 等...然后使用这些编译时整数来符合上面评论中所述的规则)?也许避免模板并做其他事情?

可以通过为四时钟显示classes 提供一个bool 模板参数来完成。例如

template <bool P>
class DescriptiveTimeDisplay : public TimeDisplay {  // #3
    std::string tell() const override { return "evening"; }
    std::string tellMaybeWithPeriod() const override { return tell() + (P ? "." : ""); }
};

并通过实例化 class 来控制是否显示句点,例如

std::make_shared<DescriptiveTimeDisplay<true>>()

对于displayCurrentTimefoobarbaz这四个函数中的每一个,都可以通过实例化这四个TimeDisplay来控制它们的显示格式具有不同 bool 模板参数的子 classes。

感谢 simon 的想法,我现在使用以下特征得到了我想要的理想解决方案:

template <int, int> struct PeriodOrNoPeriod;

template <> struct PeriodOrNoPeriod<0,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<0,1> : std::false_type {};
template <> struct PeriodOrNoPeriod<0,2> : std::true_type {};
template <> struct PeriodOrNoPeriod<0,3> : std::true_type {};

template <> struct PeriodOrNoPeriod<1,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<1,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<1,2> : std::true_type {};
template <> struct PeriodOrNoPeriod<1,3> : std::false_type {};

template <> struct PeriodOrNoPeriod<2,0> : std::true_type {};
template <> struct PeriodOrNoPeriod<2,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<2,2> : std::false_type {};
template <> struct PeriodOrNoPeriod<2,3> : std::true_type {};

template <> struct PeriodOrNoPeriod<3,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<3,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<3,2> : std::false_type {};
template <> struct PeriodOrNoPeriod<3,3> : std::false_type {};

以及一次处理所有情况的功能:

template <System::Action A, int N>
struct System::SetTimeDisplay {
    static void execute (TimeDisplay::Mode mode) {
        constexpr TimeDisplay::Mode M = static_cast<TimeDisplay::Mode>(N);
        if (mode == M)
            timeDisplay = std::make_unique<TimeDisplayClass<M, PeriodOrNoPeriod<A,M>::value>>();
        else
            SetTimeDisplay<A, N+1>::execute(mode);
    }
};

template <System::Action A>
struct System::SetTimeDisplay<A, TimeDisplay::NumTimeDiplayModes> {
    static void execute (TimeDisplay::Mode) {}  // End of recursion
};

template <System::Action A>
inline void System::action (TimeDisplay::Mode mode) {
    SetTimeDisplay<A,0>::execute(mode);
    finalAction<A>();
}

这里有完整的解决方案:

http://ideone.com/6pET9E