Julia では(例えばここで) multiple dispatch / 多重ディスパッチできるよ!と言われ、C++ ではできないよ!と言われていますが、
The choice of which method to execute when a function is applied is called dispatch. Julia allows the dispatch process to choose which of a function's methods to call based on the number of arguments given, and on the types of all of the function's arguments. This is different than traditional object-oriented languages, where dispatch occurs based only on the first argument, which often has a special argument syntax, and is sometimes implied rather than explicitly written as an argument. [1] Using all of a function's arguments to choose which method should be invoked, rather than just the first, is known as multiple dispatch. Multiple dispatch is particularly useful for mathematical code, where it makes little sense to artificially deem the operations to "belong" to one argument more than any of the others: does the addition operation in x + y
belong to x
any more than it does to y
? The implementation of a mathematical operator generally depends on the types of all of its arguments. Even beyond mathematical operations, however, multiple dispatch ends up being a powerful and convenient paradigm for structuring and organizing programs.
[1]: In C++ or Java, for example, in a method call like
obj.meth(arg1,arg2)
, the object obj "receives" the method call and is implicitly passed to the method via thethis
keyword, rather than as an explicit method argument. When the currentthis
object is the receiver of a method call, it can be omitted altogether, writing justmeth(arg1,arg2)
, withthis
implied as the receiving object.
自分が思っていた多重ディスパッチの定義だと C++ でも動くようなので、何かの理解を間違えていると思うのですがどこを間違えているのか分かりません。私は3つの polymorphism / ポリモルフィズム(ad hoc / アドホック, subtype / 部分型, parametric / パラメトリック)を Julia と C++ でそれぞれ書くならば、以下のことが行えることと理解しています。
julia
1# ad hoc polymorphism 2 3mutable struct A end 4 5mutable struct B end 6 7function adhoc(::A) 8 println("A") 9end 10 11function adhoc(::B) 12 println("B") 13end 14 15function adhoc(::A, ::A) 16 println("A A") 17end 18 19function adhoc(::B, ::B) 20 println("B B") 21end 22 23function adhoc(::A, ::B) 24 println("A B") 25end 26 27function adhoc(::B, ::A) 28 println("B A") 29end 30 31a = A() 32b = B() 33 34adhoc(a) 35adhoc(b) 36adhoc(a, a) 37adhoc(b, b) 38adhoc(a, b) 39adhoc(b, a) 40 41# subtype polymorphism 42 43abstract type Animal end 44 45mutable struct Dog <: Animal end 46 47mutable struct Cat <: Animal end 48 49function is(::Animal) 50 println("animal") 51end 52 53function is(::Cat) 54 println("cat") 55end 56 57function is(::Animal, ::Animal) 58 println("animal animal") 59end 60 61function is(::Cat, ::Animal) 62 println("cat animal") 63end 64 65function is(::Animal, ::Cat) 66 println("animal cat") 67end 68 69function is(::Cat, ::Cat) 70 println("cat cat") 71end 72 73dog = Dog() 74cat = Cat() 75 76is(dog) 77is(cat) 78is(dog, dog) 79is(cat, cat) 80is(dog, cat) 81is(cat, dog) 82 83# parametric polymorphism 84 85mutable struct Box{T} 86 object::T 87end 88 89function content(::Box{<:Animal}) 90 println("This box has an animal.") 91end 92 93function content(::Box{Cat}) 94 println("This box has a cat.") 95end 96 97box_with_dog = Box(dog) 98box_with_cat = Box(cat) 99 100content(box_with_dog) 101content(box_with_cat) 102
cpp
1#include <iostream> 2 3using std::cout, std::endl; 4 5// ad hoc polymorphism 6 7struct A {}; 8 9struct B {}; 10 11auto adhoc(A x) { 12 cout << "A" << endl; 13} 14 15auto adhoc(B x) { 16 cout << "B" << endl; 17} 18 19auto adhoc(A x, A y) { 20 cout << "A A" << endl; 21} 22 23auto adhoc(B x, B y) { 24 cout << "B B" << endl; 25} 26 27auto adhoc(A x, B y) { 28 cout << "A B" << endl; 29} 30 31auto adhoc(B x, A y) { 32 cout << "B A" << endl; 33} 34 35// subtype polymorphism 36 37struct Animal {}; 38 39struct Dog : Animal {}; 40 41struct Cat : Animal {}; 42 43auto is(Animal animal) { 44 cout << "animal" << endl; 45} 46 47auto is(Cat cat) { 48 cout << "cat" << endl; 49} 50 51auto is(Animal animal1, Animal animal2) { 52 cout << "animal animal" << endl; 53} 54 55auto is(Cat cat1, Cat cat2) { 56 cout << "cat cat" << endl; 57} 58 59auto is(Animal animal, Cat cat) { 60 cout << "animal cat" << endl; 61} 62 63auto is(Cat cat, Animal animal) { 64 cout << "cat animal" << endl; 65} 66 67// parametric polymorphism 68 69template <typename T> 70struct Box { 71 T object; 72 Box(T x) {} 73}; 74 75template <typename T> 76auto content(Box<T> box) { 77 cout << "This box has an animal." << endl; 78} 79 80auto content(Box<Cat> cat) { 81 cout << "This box has a cat." << endl; 82} 83 84int main() { 85 86 // ad hoc polymorphism 87 88 auto a = A(); 89 auto b = B(); 90 91 adhoc(a); 92 adhoc(b); 93 adhoc(a, a); 94 adhoc(b, b); 95 adhoc(a, b); 96 adhoc(b, a); 97 98 // subtype polymorphism 99 100 auto dog = Dog(); 101 auto cat = Cat(); 102 103 is(dog); 104 is(cat); 105 is(dog, dog); 106 is(cat, cat); 107 is(dog, cat); 108 is(cat, dog); 109 110 // parametric polymorphism 111 112 auto box_with_dog = Box(dog); 113 auto box_with_cat = Box(cat); 114 115 content(box_with_dog); 116 content(box_with_cat); 117} 118
多重ディスパッチはここで言うところの部分型ポリモルフィズムを1つの引数に限らず、全ての引数について行えることだと理解していて C++ だとコンパイルできないのかと思っていたのですが、このコードは以下の出力のようにどちらも自分の思ったように動きます。
text
1A 2B 3A A 4B B 5A B 6B A 7animal 8cat 9animal animal 10cat cat 11animal cat 12cat animal 13This box has an animal. 14This box has a cat.
私の理解のうち何が間違っているのでしょうか?
追記1:
julia
1function content(::Box{<:Animal}) 2 println("This box has an animal.") 3end
cpp
1template <typename T> 2auto content(Box<T> box) { 3 cout << "This box has an animal." << endl; 4}
の部分の Julia の <:Animal
の部分が C++ だと表現できてないので C++20 の機能を使って
cpp
1#include <concepts> 2 3template <typename T> 4requires std::derived_from<T, Animal> 5auto content(Box<T> box) { 6 cout << "This box has an animal." << endl; 7}
にすべきでした。
追記2:
どこが分かってないのか分かってきた気がします。Julia では abstract type
はインスタンス化できないので、Animal
型のオブジェクトは存在しません。私が Julia 脳になっているので C++ での
cpp
1Animal dog = Dog(); 2Animal cat = Cat();
がどういう意味か分かりません。 Animal ≠ Dog
, Animal ≠ Cat
ではないということでしょうか?
追記3:
文字数上限に達してしまったので別の質問をたてて、続きを書きます。リンクはこちらです。