Що таке мономорфізація в контексті C ++?


Відповіді:


141

Мономорфізація означає генерацію спеціалізованих версій загальних функцій. Якщо я напишу функцію, яка витягує перший елемент будь-якої пари:

fn first<A, B>(pair: (A, B)) -> A {
    let (a, b) = pair;
    return a;
}

а потім двічі викликаю цю функцію:

first((1, 2));
first(("a", "b"));

Компілятор згенерує дві версії first(), одна спеціалізована для пар цілих чисел і одна спеціалізована для пар рядків.

Назва походить від терміну мови програмування "поліморфізм" - означає одну функцію, яка може мати справу з багатьма типами даних. Мономорфізація - це перетворення з поліморфного в мономорфний код.


Це інша назва статичного відправлення ?
tshepang

9
@Tshepang Неправда, це більше схоже на різницю між шаблонами C ++ та загальними Java.
Тавіан Барнс

Мені здається таким самим, як і те, що ми просто називали б (неявною) спеціалізацією шаблонів на C ++. Не плутати з мономорфізмом, що було б логічною протилежністю поліморфізму у сенсі роботи з підтипом через батьківський інтерфейс.
stellarpower

17

Не впевнений, що хтось все ще дивиться на це, але в документації Rust насправді згадується, як він не дозволяє скоротити витрати за допомогою цього процесу. Від продуктивності коду з використанням дженериків :

Можливо, вам цікаво, чи існує вартість виконання, коли ви використовуєте загальні параметри типу. Хороша новина полягає в тому, що Rust впроваджує загальні засоби таким чином, що ваш код не працює повільніше, використовуючи загальні типи, ніж це було б для конкретних типів.

Руст досягає цього, виконуючи мономорфізацію коду, який використовує загальні засоби під час компіляції. Мономорфізація - це процес перетворення загального коду на конкретний код шляхом заповнення конкретних типів, які використовуються при компіляції.

У цьому процесі компілятор робить протилежні кроки, які ми використовували для створення загальної функції в лістингу 10-5: компілятор переглядає всі місця, де викликається загальний код, і генерує код для конкретних типів, з якими викликається загальний код .

Давайте подивимось, як це працює, на прикладі, який використовує перелік Option стандартної бібліотеки:

let integer = Some(5);
let float = Some(5.0);

Коли Руст компілює цей код, він виконує мономорфізацію. Під час цього процесу компілятор читає значення, які використовувались у примірниках Option, і визначає два типи Option: один - i32, а другий - f64. Як такий, він розширює загальне визначення Option на Option_i32 та Option_f64, тим самим замінюючи загальне визначення конкретними.

Мономорфізована версія коду виглядає наступним чином. Загальний параметр замінюється конкретними визначеннями, створеними компілятором:

// Filename: src/main.rs

enum Option_i32 {
    Some(i32),
    None,
}

enum Option_f64 {
    Some(f64),
    None,
}

fn main() {
    let integer = Option_i32::Some(5);
    let float = Option_f64::Some(5.0);
}

Оскільки Rust компілює загальний код у код, який вказує тип у кожному екземплярі, ми не платимо за час використання загальних кодів. Коли код працює, він працює так само, як і в тому випадку, якби ми продублювали кожне визначення вручну. Процес мономорфізації робить дженерики Rust надзвичайно ефективними під час виконання.


1
Ласкаво просимо до Stack Overflow та дякуємо за відповідь. Надаючи відповідача, який здебільшого складається із посилання, загалом корисно коротко підсумувати зміст статті, на яку пов’язано посилання. Таким чином ваша публікація залишається корисною, якщо посилання стає недійсним. Ви також частіше отримуєте голоси за вашу відповідь таким чином.
New Pagodi

4

Не впевнений у цьому; Ви могли б зробити посилання на розмову? Можливо, це було ненавмисне зауваження.

Герман міг би ввести термін для чогось на зразок спеціалізації шаблону, який генерує типи / об'єкти, які взаємопов'язані (не поліморфні чи "мономорфні") із шаблону, який є поліморфною структурою.


2

У Книзі іржі є гарне пояснення мономорфізації

Мономорфізація - це процес перетворення загального коду на конкретний код шляхом заповнення конкретних типів, які використовуються при компіляції.

З прикладу книги, якщо ви визначили змінні за допомогою Some:

let integer = Some(5);
let float = Some(5.0);

Коли Руст компілює цей код, він виконує мономорфізацію. Під час цього процесу компілятор читає значення, які використовувались у Option<T> екземплярах, і визначає два типи Option<T>: один є, i32а інший - f64. Як такий, він розширює загальне визначення Option<T>в Option_i32і Option_f64, таким чином , замінивши загальне визначення з конкретними з них.

Мономорфізована версія коду виглядає наступним чином. Загальний Option<T>змінено замінено конкретними визначеннями, створеними компілятором:

Ім'я файлу: src / main.rs

enum Option_i32 {
    Some(i32),
    None,
}

enum Option_f64 {
    Some(f64),
    None,
}

fn main() {
    let integer = Option_i32::Some(5);
    let float = Option_f64::Some(5.0);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.