C++ C++;?

C++ C++;?,c++,coding-style,C++,Coding Style,我了解使用电脑的技巧。我只是不明白它有什么好处 声明的动机是: 我们牺牲了一些动态多态性的灵活性来提高速度 但是为什么要为这么复杂的事情而烦恼呢 我最好的猜测是代码中没有语义上的差异,这只是一个好的C++风格的问题。p> 萨特在《代码》中写道:Excel C++风格:第18章: 更喜欢将虚拟函数私有化 当然还附带了一个彻底的解释,为什么这是一种好的风格 在本指南中,第一个示例为良好,因为: 示例中的void implementation()函数可以假装是虚拟的,因为它在这里执行类的自定义。因此,

我了解使用电脑的技巧。我只是不明白它有什么好处

声明的动机是:

我们牺牲了一些动态多态性的灵活性来提高速度

但是为什么要为这么复杂的事情而烦恼呢

我最好的猜测是代码中没有语义上的差异,这只是一个好的C++风格的问题。p>

萨特在《代码》中写道:Excel C++风格:第18章<代码>:

更喜欢将虚拟函数私有化

当然还附带了一个彻底的解释,为什么这是一种好的风格

在本指南中,第一个示例为良好,因为:

示例中的
void implementation()
函数可以假装是虚拟的,因为它在这里执行类的自定义。因此,它应该是私人的

第二个例子是坏的,因为:

我们不应该干预公共接口来执行定制

我的问题是:

  • 关于静态多态性,我遗漏了什么?这一切都是关于好的C++风格吗?< /LI>
  • 什么时候使用?有什么指导方针
  • 关于静态多态性,我遗漏了什么?这一切都是关于好的C++风格吗?< /P> 静态多态性和运行时多态性是不同的东西,实现不同的目标。它们在技术上都是多态性的,因为它们根据某种类型决定执行哪段代码。运行时多态性将绑定某物的类型(以及运行的代码)推迟到运行时,而静态多态性在编译时完全解决

    这导致了每种方法的优缺点。例如,静态多态性可以在编译时检查假设,或者在其他情况下不会编译的选项中进行选择。它还向编译器和优化器提供了大量信息,编译器和优化器可以完全了解调用的目标和其他信息。但是静态多态性要求编译器可以在每个翻译单元中检查实现,可能导致二进制代码大小膨胀(模板是复制粘贴),并且不允许在运行时进行这些确定

    例如,考虑一些类似于<代码> STD::前进< /代码>:

    template<typename Iterator>
    void advance(Iterator& it, ptrdiff_t offset)
    {
        // If it is a random access iterator:
        // it += offset;
        // If it is a bidirectional iterator:
        // for (; offset < 0; ++offset) --it;
        // for (; offset > 0; --offset) ++it;
        // Otherwise:
        // for (; offset > 0; --offset) ++it;
    }
    
    但这会迫使
    DoAndLog
    在头文件中实现,这可能是不切实际的。它还要求
    StreamT
    的所有可能实现在编译时都是可见的,这可能不是真的——运行时多态性可以跨DLL或其他边界工作(尽管不建议这样做)


    什么时候使用?有什么指导方针

    这就像有人来问你“当我写一个句子时,我应该用复句还是简单句?”?或者一位画家说“我应该一直使用红色油漆还是蓝色油漆?”没有正确的答案,这里也没有一套可以盲目遵循的规则。您必须查看每种方法的优缺点,并决定哪种方法最适合您的特定问题领域


    至于CRTP,它的大多数用例是允许基类提供派生类方面的内容;e、 g.Boost的迭代器。基类需要有
    DerivedClass操作符+++(){/*增量和return*this*/}
    这样的内容——根据成员函数签名中的派生来指定


    它可以用于多态性目的,但我没有看到太多。您提供的链接提到boost迭代器是静态多态性的一个示例。STL迭代器也展示了这种模式。让我们来看看一个例子,并考虑为什么这些类型的作者认为这个模式是合适的:

    #include <vector>
    #include <iostream>
    using namespace std;
    void print_ints( vector<int> const& some_ints )
    {
        for( vector<int>::const_iterator i = some_ints.begin(), end = some_ints.end(); i != end; ++i )
        {
            cout << *i;
        }
    }
    
    传统的动态多态性无法提供上述实现

    一个相关且重要的术语是参数多态性。这允许您在Python中实现类似的API,您可以使用C++中奇怪的重复模板模式。希望这是有帮助的


    我认为值得一试所有这些复杂性的根源,以及为什么像Java和C#这样的语言大多试图避免它:类型擦除!在C++中,没有有用的信息包含有用的信息>代码>对象< /代码>类型。相反,我们有
    void*
    ,一旦你有了
    void*
    你就真的什么都没有了!如果你有一个界面会退化到
    void*
    ,唯一的恢复方法就是做出危险的假设或保留额外的类型信息。

    虽然在某些情况下静态多态性是有用的(其他答案列出了一些),但我通常认为这是一件坏事。为什么?因为您不能再实际使用指向基类的指针,所以必须始终提供一个模板参数来提供精确的派生类型。在这种情况下,您也可以直接使用派生类型。而且,坦率地说,静态多态性不是面向对象的意义所在


    静态多态性和动态多态性之间的运行时差异恰恰是两个指针的解引用(如果编译器真的在基类中内联dispatch方法,如果出于某种原因没有内联,静态多态性会更慢)。这其实并不昂贵,特别是因为第二次查找实际上应该总是命中缓存。总而言之,这些查找通常比函数调用本身更便宜,而且获得动态多态性提供的真正灵活性肯定是值得的。

    请注意,该函数不是虚拟的,结果是,通过基类指针调用
    base::interface
    而不是
    Derived::interface
    ——您只是隐藏了继承的名称。这里没有多态性。实际上我对此也很好奇。我的理解是
    template<typename Iterator>
    void advance(Iterator& it, ptrdiff_t offset)
    {
        // If it is a random access iterator:
        // it += offset;
        // If it is a bidirectional iterator:
        // for (; offset < 0; ++offset) --it;
        // for (; offset > 0; --offset) ++it;
        // Otherwise:
        // for (; offset > 0; --offset) ++it;
    }
    
    template<typename Iterator>
    void advance_impl(Iterator& it, ptrdiff_t offset, random_access_iterator_tag)
    {
        // Won't compile for bidirectional iterators!
        it += offset;
    }
    
    template<typename Iterator>
    void advance_impl(Iterator& it, ptrdiff_t offset, bidirectional_iterator_tag)
    {
        // Works for random access, but slow
        for (; offset < 0; ++offset) --it; // Won't compile for forward iterators
        for (; offset > 0; --offset) ++it;
    }
    
    template<typename Iterator>
    void advance_impl(Iterator& it, ptrdiff_t offset, forward_iterator_tag)
    {
         // Doesn't allow negative indices! But works for forward iterators...
         for (; offset > 0; --offset) ++it;
    }
    
    template<typename Iterator>
    void advance(Iterator& it, ptrdiff_t offset)
    {
        // Use overloading to select the right one!
        advance_impl(it, offset, typename iterator_traits<Iterator>::iterator_category());
    }  
    
    void DoAndLog(std::ostream& out, int parameter)
    {
        out << "Logging!";
    }
    
    template<typename StreamT>
    void DoAndLog(StreamT& out, int parameter)
    {
        out << "Logging!";
    }
    
    #include <vector>
    #include <iostream>
    using namespace std;
    void print_ints( vector<int> const& some_ints )
    {
        for( vector<int>::const_iterator i = some_ints.begin(), end = some_ints.end(); i != end; ++i )
        {
            cout << *i;
        }
    }
    
    template<typename T>
    class const_iterator_base
    {
    public:
        const_iterator_base():{}
    
        T::contained_type const& operator*() const { return Ptr(); }
        T::contained_type const& operator->() const { return Ptr(); }
        // increment, decrement, etc, can be implemented and forwarded to T
        // ....
    private:
        T::contained_type const* Ptr() const { return static_cast<T>(this)->Ptr(); }
    };