Follow me to learn c++ advanced articles - Eleven duck types of template metaprogramming

1. Duck type

The duck type did not appear from c++, and the term duck typing is more common in Python. So what is duck typing? It is a style of dynamic typing, as long as the characteristics of an object (its method and property set) are consistent with a certain type, it is considered to be a certain type. It is generally more common in dynamically compiled languages, such as Python, Js, etc. In static languages ​​such as c#, c++, etc., some specific methods are needed to support the implementation.
Duck typing emphasizes the mapping from object to type, that is to say, from concrete to abstract, rather than the common mapping from abstract to concrete. It can be understood that objects implemented through class or interface inheritance must be implemented by them. Conversely, if an object (runtime) does not know how it is implemented, but it is found to be similar to a certain class or interface (such as the method and attribute set mentioned above), then it is also considered to be implemented.
This is a bit too much. In fact, it means that the necessary and sufficient conditions are forcibly determined to be consistent under certain circumstances.
First look at an implementation in python:

class Duck:
    def shout(self):
        print("Duck shot")

    def fly(self):
        print("Duck  fly")

class Goose:
    def shout(self):
        print("Goose shout")

    def fly(self):
        print("Goose fly")

def test(bird):
    bird.shout()
    bird.fly()

test(Duck())
test(Goose())

The advantage of duck typing is that it implements polymorphism without using inheritance. This technique has been introduced in the previous metaprogramming, including CRTP, and the benefits of polymorphism without inheritance have long been clear.
Looking back, in fact, there is no duck type in C++. However, in C++ template metaprogramming, an implementation similar to duck typing can be achieved, which can also be classified into duck typing.

Second, the implementation in the template

Let's look at the simplest example:

template<typename T>
bool max(const T& t1, const T& t2)
{
    return t1 > t2;
}

This is the simplest template function, and there is nothing unreasonable about this function alone. But in fact, there is a condition implied in this function, that is, the type T must be able to compare sizes. And T itself is a template class, so there is a problem. If a custom class is passed in and this class does not implement operator >, what will happen to the program? If this is not clear enough to illustrate the control of duck typing (methods and property sets), take a look at the previous example:

template<typename T>
auto len (T const&& t) -> decltype((void)(t.size()) , T::size_type)
{
    return t.size();
}

The form of this function controls whether t has the function size() through decltype. This is not the method collection control in duck typing. By extension, it can be used to judge the data structure similar to the duck type. But it should be noted that c++ metaprogramming itself does not support duck typing, so you must pay attention to this. If you need to use it in practical applications, you need to indirectly implement duck typing by encapsulating data structures (especially for non-type parameters and basic data types).
After c++20, the above method can be optimized in a conceptual way

template<typename T>
concept func_ask = requires(T t)
{
    t.must();
};

//......省略
template<func_ask T>
void must(const T& t)
{
    t.must();
}
//省略

The complete code is in the relevant paragraphs in the previous "Several Applications of Concepts" and "Concepts in C++20".
In fact, duck typing does not have much advantage in strongly typed languages ​​such as C++. It makes the code that is already difficult to maintain even worse. But it does not mean that the duck type is useless, it is good to use it where it should be used. Just like employing people, use their strengths rather than their weaknesses.

3. Summary

In fact, this chapter is really not very useful for metaprogramming. The reason why I still write this article is very simple, to form a horizontal comparison between some language-level things and specific technical implementations. As mentioned earlier, the development of any language is not imagined out of thin air, but must be based on a certain idea that has been tempered repeatedly in practice. It is a connection between the previous and the next. It solves some problems and may also introduce some new problems, but on the whole there must be an obvious improvement in a certain aspect compared to other languages, which can make this course Language can be applied in the software world.
If you are interested, you can look back at the language from assembly to C to C++. Then there are explosive Java, c#, JS, Ts, Go, Python, Rust and so on. Knowing this, when learning a new language, you will understand that this language is not a complete new technology. The more languages ​​you master, the more you will find that there are many familiar figures in it. If you want to give an example, the most obvious one is Lambda expression, which is supported by almost all mainstream languages ​​now. Understand.

Guess you like

Origin blog.csdn.net/fpcc/article/details/129479963