Является C++ различных компиляторов виртуальными несовместимыми реализациями наследования?

1113 Интересно, есть ли смысл пытаться построить это с нуля? Посмотрим. Вот базовая рекурсивная факториальная функция:

function factorial(n) {
    return n == 0 ? 1 : n * factorial(n - 1);
}

Давайте проведем рефакторинг и создадим новую функцию под названием fact, которая вместо выполнения самого вычисления возвращает анонимную функцию факториального вычисления:

function fact() {
    return function(n) {
        return n == 0 ? 1 : n * fact()(n - 1);
    };
}

var factorial = fact();

Это немного странно, но в этом нет ничего плохого. Мы просто генерируем новую факториальную функцию на каждом шаге.

Рекурсия на этом этапе все еще довольно явно. Функция fact должна знать свое имя. Давайте параметризуем рекурсивный вызов:

function fact(recurse) {
    return function(n) {
        return n == 0 ? 1 : n * recurse(n - 1);
    };
}

function recurser(x) {
    return fact(recurser)(x);
}

var factorial = fact(recurser);

Это здорово, но recurser все еще нужно знать его собственное имя. Давайте также параметризуем это:

function recurser(f) {
    return fact(function(x) {
        return f(f)(x);
    });
}

var factorial = recurser(recurser);

Теперь, вместо непосредственного вызова recurser(recurser), давайте создадим функцию-обертку, которая возвращает ее результат:

function Y() {
    return (function(f) {
        return f(f);
    })(recurser);
}

var factorial = Y();

Теперь мы можем избавиться от recurser имя в целом; это просто аргумент внутренней функции Y, который можно заменить самой функцией:

function Y() {
    return (function(f) {
        return f(f);
    })(function(f) {
        return fact(function(x) {
            return f(f)(x);
        });
    });
}

var factorial = Y();

Единственное внешнее имя, на которое все еще ссылаются, это fact, но теперь должно быть ясно, что это легко параметризовать, также, создавая полное, общее решение:

function Y(le) {
    return (function(f) {
        return f(f);
    })(function(f) {
        return le(function(x) {
            return f(f)(x);
        });
    });
}

var factorial = Y(function(recurse) {
    return function(n) {
        return n == 0 ? 1 : n * recurse(n - 1);
    };
});
5
задан j_random_hacker 24 June 2009 в 14:29
поделиться

4 ответа

Я не думал, что вы можете рассчитывать на совместимость любых построенных классов между компиляторами. Утверждает ли Borland, что они могут загружать классы, созданные MSVC, и взаимодействовать с ними. Если так, похоже, у них есть ошибка. Насколько мне известно, ничего о точной структуре VTable не указано в спецификации C ++, поэтому не ожидается, что она будет работать во всех компиляторах.

8
ответ дан 18 December 2019 в 14:50
поделиться

Я с подозрением отношусь к аргументу void ** . Использование указателей void приводит к потере информации о типе.

Если вы работаете с множественным наследованием, информация о типе может быть важной. Рассмотрим:

class Foo { ... };
class Bar { ... };

class Both: public Foo, public Bar { ... };

Предположим, что внутренняя структура экземпляра Both - это экземпляр Foo, за которым следует экземпляр Bar. Я могу без проблем передать Both * методу, ожидающему Foo *. Я также могу передать Both * методу, ожидающему Bar *, при условии, что я настрою указатель так, чтобы он указывал на встроенную Bar. Я могу сделать это надежно, потому что я знаю, что работаю с обоими.

Теперь:

Foo *foo = new Both(...);
Bar *bar = new Both(...);

void *p = foo;
void *q = bar;

Both *both = (which) ? (Both*)p : (Both*)q;

Итак: как мне узнать, как настроить либо p, либо q, когда я назначаю «оба»? Я не могу, потому что информация о типе теряется при прохождении указателя void.

Вариант этой проблемы может быть связан с вашими проблемами.

0
ответ дан 18 December 2019 в 14:50
поделиться

Наверное, не ответит на ваш вопрос ... Но настоятельно рекомендуется НЕ использовать множественное наследование, как вы это делаете (так называемый «ужасный алмаз»).

-2
ответ дан 18 December 2019 в 14:50
поделиться

В отличие от C, для C ++ отсутствует кросс-компиляторный ABI - компиляторы могут реализовать виртуальное наследование (и даже обычное наследование) любым способом.

В результате: вызов функций C ++ через компиляторы не гарантируется. Я знаю, что это некрасиво, но если вы хотите, чтобы ваша DLL успешно взаимодействовала с несколькими компиляторами, вам, вероятно, лучше предоставить набор простых extern «C» функций и составленные вручную таблицы указателей функций.

Примечание. Компиляторы, которые поддерживают создание COM-объектов (или имеют возможность делать это), имеют более ограниченные возможности в своих макетах объектов. (Я знаю, что последние версии MSVC ++ создают COM-совместимые объекты, по крайней мере, в большинстве случаев - хотя я не уверен, распространяется ли виртуальное наследование.)

7
ответ дан 18 December 2019 в 14:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: