質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Julia

Juliaとは、科学技術計算に特化した、高水準・高性能な動的プログラミング言語です。オープンソースとして公表されており、書き易く動きが早いことが特徴です。

Q&A

2回答

3009閲覧

3つのポリモルフィズムと多重ディスパッチについて C++ と Julia での理解がどこか間違っていると思われるがどこか分からない。

Paalon

総合スコア232

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Julia

Juliaとは、科学技術計算に特化した、高水準・高性能な動的プログラミング言語です。オープンソースとして公表されており、書き易く動きが早いことが特徴です。

1グッド

0クリップ

投稿2020/03/14 07:12

編集2020/03/20 18:48

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 the this keyword, rather than as an explicit method argument. When the current this object is the receiver of a method call, it can be omitted altogether, writing just meth(arg1,arg2), with this 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

文字数上限に達してしまったので別の質問をたてて、続きを書きます。リンクはこちらです。

s.k👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t_obara

2020/03/24 03:17

こちらはクローズされてはいかがでしょうか。
guest

回答2

0

C++の場合、親のポインタを介すと

c++

1Animal *dog = new Dog(); 2Animal *cat = new Cat(); 3is(*dog, *cat); // => animal animal

になります。

ここで仮想メンバ関数を用いていた場合

c++

1class Animal{ 2 virtual std::string is(Animal*){ return "animal animal"; } 3}; 4class Dog : Animal{ 5 virtual std::string is(Animal*){ return "dog animal"; } 6}; 7dog->is(*cat); // => dog animal

は第一引数の型はディスパッチに使われますので自動で判別できます。


現実的に問題になるのは配列の場合でしょうか

c++

1Animal *animals[] = { new Dog, new Cat };

みたいな事が言いたいのかなと推測します。


追記2

Julia では abstract type はインスタンス化できないので、Animal 型のオブジェクトは存在しません。

そういえばC++の方は抽象クラスじゃなかったですね。

c++

1struct Animal{ virtual ~Animal() = 0; };

Animal dog = Dog();
Animal cat = Cat();

Dogクラスのインスタンスを作って、コピーコンストラクタでAnimal型の変数にコピーし
結果として、Animal型の変数になりDogとしての部分は切り落とされます。

julia

1dog = Animal() 2dog .= Dog()

みたいな感じですかね

投稿2020/03/14 09:05

編集2020/03/15 00:45
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Paalon

2020/03/14 13:43 編集

```cpp auto dog = Dog() auto cat = Cat() std::vector<Animal> xs {dog, cat, dog, cat}; for (auto x: xs) { is(x); } ``` が ```text animal cat animal cat ``` ではなく ```text animal animal animal animal ``` になってしまうということですか?
asm

2020/03/14 14:55

そもそも、std::vector<Animal> xs {dog, cat, dog, cat};の時点でかなりマズいですがそういう事です。
Paalon

2020/03/20 18:45

追記を書こうとしたのですが、文字数上限に達してしまったので新しく質問を作り、続きを書きました。https://teratail.com/questions/248408 よろしければ見ていただけると嬉しいです。
guest

0

こんにちは。

ポリモーフィズムの用語についてWikipediaのこの記事の定義に従うと、PaalonさんのC++のコードは全てアドホック(オーバーロード)です。
is()はAnimal, Dog, Catクラスのメンバ関数でさえなく、まして仮想関数になっていませんので、部分型多相ではなくオーバーロード(=アドホック)です。
content()は関数テンプレート(≒パラメータ多相)ではあるのですが、単なるオーバーロードとして使っています。

ところで、多重ティスパッチについてWikipediaのこの記事の冒頭の定義を見る限り、多重ディスパッチはオーバーロードで実現可能です。
しかし、何故か非staticなメンバ関数を持ち出して、これはthis引数を1つしか取れないことを取り上げて単一ディスパッチと呼んでいます。非staticなメンバ関数は「それが便利」なのでthisを特別扱いするものです。
インスタンスに縛られずにオーバーロードしたい場合は、staticメンバ関数やグローバル関数を使えばよいだけです。特に制約はありません。

投稿2020/03/14 09:11

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Paalon

2020/03/14 13:52 編集

`is` が `Animal`, `Dog`, `Cat` のメンバ関数であることが部分型ポリモルフィズムの必要条件なのですか? 型 `Dog` を持つ `dog` について `is(Dog dog)` が定義されていないのに、`is(dog)` と書くと `Dog` は `Animal` を継承しているから、`is(Animal animal)` が参照されることが部分型ポリモルフィズムであると考えていたのですが違うのでしょうか? `content` が関数テンプレート(≒ パラメータ多相)であるのであれば、`content` はパラメトリックポリモルフィズムになっていると言うのは間違っているのでしょうか?
Chironian

2020/03/14 15:25

> と考えていたのですが違うのでしょうか? 回答でリンクしたWikipediaの記事の用語の定義に従うと、違います。 > と言うのは間違っているのでしょうか? 同定義に従うと、間違っています。 同記事によるとsubtype polymorphismの定義を見る限り、これは仮想関数を使ったポリモーフィズムのことを指しています。動的ポリモーフィズムとも呼ばれます。 また、テンプレートを使ったポリモーフィズムは、型パラメータに渡した型が「同じ名前」のメンバ(関数でも変数でも型でも良い)を持っている時、その型パラメータのインスタンスに対して「同じ名前」でそのメンバを呼び出せることを指す場合が多いです。静的ポリモーフィズムとも呼ばれます。 ↓ぴったりの解説とサンプル・コードがありました。 https://qiita.com/Riyaaaa_a/items/887f6190e710c6410994 これらのサンプル・コードとPaalonさんのコードを見比べてみて下さい。ご理解頂けると思います。
Paalon

2020/03/20 18:49

追記を書こうとしたのですが、文字数上限に達してしまったので新しく質問を作り、続きを書きました。https://teratail.com/questions/248408 よろしければ見ていただけると嬉しいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問