Static duck typing in C++

后端 未结 5 2292
日久生厌
日久生厌 2021-02-15 14:45

C++ has some sort of duck typing for types given by template parameters. We have no idea what type DUCK1 and DUCK2 will be, but as long as they can

相关标签:
5条回答
  • 2021-02-15 14:53

    Concerning questions 1 and 2: since C++14 you can omit explicit template <typename ... boilerplate and use auto, but only in lambdas:

    auto let_them_quack = [] (auto & donald, auto & daisy){
        donald.quack();
        daisy.quack();
    };
    

    (yes, I prefer references to pointers). GCC allows to do so in usual functions as an extension.

    For the question 3, what you are talking about are called concepts. They existed in C++ for a long time, but only as a documentational term. Now the Concepts TS is in progress, allowing you to write something like

    template<typename T>
    concept bool Quackable = requires(T a) {
        a.quack();
    };
    
    void let_them_quack (Quackable & donald, Quackable & daisy);
    

    Note that it is not yet C++, only a technical specification in progress. GCC 6.1 already seems to support it, though. Implementations of concepts and constraints using current C++ are possible; you can find one in boost.

    0 讨论(0)
  • 2021-02-15 14:53

    We only need to write one version of the function:

    #include <utility>
    
    template<typename... Quackers>
    void let_them_quack(Quackers&& ...quackers) {
      using expand = int[];
    
      void(expand { 0, (std::forward<Quackers>(quackers).quack(), 0)... });
    }
    
    struct Duck {
      void quack() {}
    };
    
    int main()
    {
      Duck a, b, c;
      let_them_quack(a, b, c, Duck());
    }
    
    0 讨论(0)
  • 2021-02-15 14:57

    You will be able to make it look preetier with concept (not yet in standard - but very close):

    http://melpon.org/wandbox/permlink/Vjy2U6BPbsTuSK3u

    #include <iostream>
    
    template<typename T>concept bool ItQuacks(){
        return requires (T a) {
            { a.quack() } -> void;
        };
    }
    
    void let_them_quack2(ItQuacks* donald, ItQuacks* daisy){
      donald->quack();
      daisy->quack();
    }
    
    struct DisneyDuck {
        void quack(){ std::cout << "Quack!";}
    };
    
    struct RegularDuck {
        void quack(){ std::cout << "Quack2!";}
    };
    
    struct Wolf {
        void woof(){ std::cout << "Woof!";}
    };
    
    int main() {
        DisneyDuck q1, q2;
        let_them_quack2(&q1, &q2);
    
        RegularDuck q3, q4;
        let_them_quack2(&q3, &q4);    
    
        //Wolf w1, w2;
        //let_them_quack2(&w1, &w2);    // ERROR: constraints not satisfied
    }
    

    output:

     Quack!Quack!Quack2!Quack2!
    

    As you can see, you will be able to: omit writing a template parameter list, ItQuacks is quite explicit so types are never used and that it's only the interface that matters takes place. This I'd like to have sort of an interface annotation/check. also takes place, concept use will also give you meaningfull error message.

    0 讨论(0)
  • 2021-02-15 15:14

    I'd like to omit writing a template parameter list that is repetitive and mostly meaningless (Just imagine what would happen if there are 7 ducks...)

    For that you could use variadic templates and do something like the following:

    template<typename DUCK>
    void let_them_quack(DUCK &&d) {
      d.quack();
    }
    
    template<typename DUCK, typename... Args>
    void let_them_quack(DUCK &&d, Args&& ...args) {
      d.quack();
      let_them_quack(std::forward<Args>(args)...);
    }
    

    Live Demo

    0 讨论(0)
  • 2021-02-15 15:14

    #2 and #3 are sort of taken care of by the fact that the code will not compile, and throw a compilation error, if the given classes don't implement the interface. You could also make this formal:

    class duck {
    
    public:
       virtual void quack()=0;
    };
    

    Then declare the parameters to the function as taking a pointer to a duck. Your classes will have to inherit from this class, making the requirements for let_them_quack() crystal clear.

    As far as #1 goes, variadic templates can take care of this.

    void let_them_quack()
    {
    }
    
    template <typename ...Args>
    void let_them_quack(duck* first_duck, Args && ...args) {
      first_duck->quack();
      let_them_quack(std::forward<Args>(args)...);
    }
    
    0 讨论(0)
提交回复
热议问题