C ++在模板化类上继承了运算符'<<'

时间:2019-12-02 20:10:29

标签: c++ inheritance operator-overloading operator-keyword

我有以下C ++代码,这些代码演示了我的问题。 我的目标是在继承的类中重写流运算符,以便允许我根据对象类型打印特定的流:

user

目前,代码尚未编译:

#include <iostream>
#include <unordered_set>

using namespace std;

template <typename T>
class Base {
    public:
        Base(){}
        Base(T n): value_(n){}

        friend inline ostream &operator<<(ostream &os, const Base &b) {
            b.to_str(os);
            return os;
        }

    protected:

        T value_;

        // All object should implement this function
        virtual void to_str(ostream& os) const {
            os << value_;
        }
};

template <typename T>
class Child: public Base<T> {
    public:
        Child(T n): Base<T>(n){}

    protected:
        void to_str(ostream& os) const override {
         os << "{";
            for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
                if(v != this->value_.begin())
                    os << ",";
                os << (*v);
            }
            os << "}";
        }
};

int main()
{
    Base<string> b("base");
    Child<unordered_set<string>> c({"child"});
    cout << "b: " << b << endl;
    cout << "c: " << c << endl;

    return 0;
}

编译器似乎使用了基类的虚拟方法 to_str(),而不是Child类的替代方法。 如果我对基本 to_str()函数的主体进行注释,则它会编译并打印unordered_set Child类的正确结果,但对于基本实现,它什么都不打印。 因此,覆盖有效,但是为什么当基 to_str()具有主体时不能编译?

如何强制编译器使用派生自孩子(孩子)的孩子?

致谢

3 个答案:

答案 0 :(得分:1)

即使从未运行过基础to_str,也会对其进行编译。

所以

    virtual void to_str(ostream& os) const {
        os << value_;
    }

无法编译。

使用vtable创建类型时,即使没有调用它们,其条目也会被填充(好吧,我相信标准说“可以”)。这与“普通”模板类不同,在“普通”模板类中,未使用的方法的主体“已跳过”。

您可能希望CRTP具有编译时多态性。

template <class T, class D_in=void>
class Base {
    using D=std::conditional_t< std::is_same<D_in,void>{}, Base, D_in >;
    public:
        Base(){}
        Base(T n): value_(n){}

        friend inline ostream &operator<<(ostream &os, const Base &b) {
            static_cast<D const&>(b).to_str(os);
            return os;
        }

    protected:

        T value_;

        // All object should implement this function
        void to_str(ostream& os) const {
            os << value_;
        }
};

template <typename T>
class Child: public Base<T, Child<T>> {
    friend class Base<T, Child<T>>;
    public:
        Child(T n): Base<T>(n){}

    protected:
        void to_str(ostream& os) const {
         os << "{";
            for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
                if(v != this->value_.begin())
                    os << ",";
                os << (*v);
            }
            os << "}";
        }
};

或类似的东西。

答案 1 :(得分:0)

虚拟呼叫实际上确实发生。您可以通过将代码更改为

来查看
template <typename T>
class Base {
    public:
        Base(){}
        Base(T n): value_(n){}

        friend inline ostream &operator<<(ostream &os, const Base &b) {
            b.to_str(os);
            return os;
        }

    protected:

        T value_;

        virtual void to_str(ostream& os) const = 0;
};

template <typename T>
class Child: public Base<T> {
    public:
        Child(T n): Base<T>(n){}

    protected:
        void to_str(ostream& os) const override {
         os << "{";
            for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
                if(v != this->value_.begin())
                    os << ",";
                os << (*v);
            }
            os << "}";
        }
};

int main()
{
    Child<unordered_set<string>> c({"child"});
    cout << "c: " << c << endl;
    return 0;
}

实际上发生的是,在Base中,编译器将淘汰

Base<unordered_set<string>>::to_str

,该函数无效,因为未定义os << value_。如您所见,您需要使它成为纯虚拟的,或者放入无论如何value_都可以编译的存根。

答案 2 :(得分:0)

这是我最终通过使用“纯虚拟解决方案”实现的, 测试了一些更基本的类型:

#include <iostream>
#include <unordered_set>

using namespace std;

template <typename T>
class Base_ {

    public:
        Base_(){}
        Base_(T n): value_(n){}

        friend inline ostream &operator<<(ostream &os, const Base_ &b) {
            b.to_str(os);
            return os;
        }

    protected:
        T value_;

        // All object should implement this function
        virtual void to_str(ostream& os) const = 0;
};

template <typename T>
class Base: public Base_<T> {
    public:
        Base(){}
        Base(T n): Base_<T>(n){}

    protected:

        // All object should implement this function
        void to_str(ostream& os) const override {
            os << this->value_;
        }
};

template <typename T>
class Child: public Base_<T> {
    public:
        Child(T n): Base_<T>(n){}

    protected:
        void to_str(ostream& os) const override {
         os << "{";
            for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
                if(v != this->value_.begin())
                    os << ",";
                os << (*v);
            }
            os << "}";
        }
};

template <typename T>
class Boolean: public Base_<T> {
    public:
        Boolean(T n): Base_<T>(n){}

    protected:
        void to_str(ostream& os) const override {
         os << (this->value_ ? "true" : "false");
        }
};

int main()
{
    Base<string> s("string");
    Base<int> i(42);
    Boolean<bool> b(true);
    Child<unordered_set<string>> u({"child1", "child2"});
    cout << "s: " << s << endl;
    cout << "i: " << i << endl;
    cout << "b: " << b << endl;
    cout << "u: " << u << endl;

    return 0;
}

结果:

  

s:字符串
  我:42
  b:是的
  u:{child2,child1}