c++ - 如何使用模板正确重写这个使用继承的 C++ 代码

标签 c++ c++11

我有一个 C++ 代码,目前看起来是这样的:有一个类层次结构可以执行一些比较,还有一个列表类使用它。使用哪个比较操作是在运行时根据某个模式对象确定的。这是结构:

class A{
    bool doComparison(const string& s1, const string& s2) const=0;
}

class B: public A{
    bool doComparison(const string& s1, const string& s2) const {
        ...
    }
}

class C: public A{
    bool doComparison(const string& s1, const string& s2) const {
        ...
    }
}

template <class, S>
public FancyList{
    shared_ptr<A> z_;
    vector<S> v;

    FancyList(shared_ptr<A> z) : z_(z);

    void DoSmth(){
        ....
        z_->doComparison(arg1, arg2);
    }

}

typedef FancyList<string> FancyStringList;

// Determine which comparison to use at runtime   
shared_ptr<A> c = nullptr; 
    switch(type):
        case int: 
          c = make_shared<B>();
          break;
        case double:
          c = make_shared<B>();
          break;
    FancyStringList l(c);
    l.push_back("stuff");

C# 曾经是我的主要语言,所以这段代码对我来说似乎没问题。但有人告诉我,这种方法的问题在于它使用虚函数,因此在方法调用中会有轻微的开销。重组这段代码的正确 C++ 方式是什么,这样就不需要有这个类层次结构,也不需要使用虚函数?

最佳答案

与您想要的相反,虚函数的开销是不可避免的,因为调用哪个实际函数的决定是在运行时做出的。

如果始终在运行时做出决定,则编译器无法将函数调用硬编码到生成的机器代码中。它必须是一个间接函数调用:使用指针指向一个函数,并在函数调用之前取消引用该指针。虚函数只是进行间接函数调用的一种方式。

模板是一种告诉编译器在编译时生成代码的方法。当在编译时做出决定时,模板所能做的就是不引入开销。它无法帮助您删除必须在运行时完成的工作。


如果您仍然对使用模板感兴趣,您可以考虑将比较器作为模板参数。

template <class T, class Comparator>
class MyList
{
    std::vector<T> vec;
    Comparator comp;

public:
    void do_thing(const T& a, const T& b)
    {
        vec.push_back(a);
        vec.push_back(b);
        bool x = comp(vec[0], vec[1]); // for example
        std::cout << x;
    }
};

在比较器类中,重载函数调用运算符

class Compare1
{
public:
    bool operator()(const std::string& lhs, const std::string& rhs) const
    {
        return lhs < rhs;
    }
};

class Compare2
{
public:
    bool operator()(const std::string& lhs, const std::string& rhs) const
    {
        return lhs.size() < rhs.size();
    }
};

int main()
{
    MyList<std::string, Compare1> myli1;
    myli1.do_thing("a", "b");

    MyList<std::string, Compare2> myli2;
    myli2.do_thing("c", "d");
}

您甚至可以将间接函数调用隐藏在比较器类后面。但它并没有消除开销。

class A
{
public:
    virtual bool doComparison(const std::string& s1, const std::string& s2) const=0;
    virtual ~A() = default;
};

class PolymorphicComparator
{
private:
    std::shared_ptr<A> comp;
public:
    PolymorphicComp(std::shared_ptr<A> c) : comp(c) {}

    bool operator()(const std::string& lhs, const std::string& rhs) const
    {
        return comp->doComparison(lhs, rhs);
    }
};

关于c++ - 如何使用模板正确重写这个使用继承的 C++ 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44382786/

相关文章:

c++ - 如何设置这个makefile

c++ - 在不使用外部库的情况下替换字符串列表中的整个单词

c++ - 继承成员函数指针

c++ - 我什么时候应该声明一个没有 noexcept 的移动构造函数?

winapi - 如何将 std::chrono::high_resolution_clock::now() 转换为 Windows 文件时间(以及反向转换)

c++ - 导致崩溃的C++ throw 语句

c++ - 引用是存储在堆上还是栈上?

c++ - std::foward 的第二次重载(cppreference.com 上的示例)

C++ Visual Studio 2017 使用我的对象 vector 获取错误代码 C3867

c++ - 无法使用统一初始化复制 std::vector<std::function<void ()>>。这个对吗?