(Это довольно скучный вопрос, но я суммировал его внизу.)
Я хочу написать класс (на C ++), который выполняет тесты для объектов какого-то неизвестного типа, происходящих из очень скелетного базового класса. Идея состоит в том, что объект этого класса инициализируется с «ожидаемым» результатом, а затем вызывается много раз, сохраняя результат и сравнивая его с ожидаемым. Вся упаковка должна выглядеть примерно так:
struct test_input
{
virtual ~test_input() = 0;
};
struct test_output
{
virtual bool operator== (const test_output&) = 0;
virtual ~test_output() = 0;
};
typedef test_output& (*test_function)(test_input&);
class test
{
const test_input &data;
const test_output &expected;
test_output *result;
test(test_input &i, test_output &o)
: data(i), expected(o), result(NULL)
{}
bool operator() (test_function &trial)
{ return *(result = &trial(data)) == expected; }
};
// Example usage
class ti_derived : public test_input { /* ... */ };
class to_derived : public test_output { /* ... */ };
to_derived& some_function_one(ti_derived &arg) { /* ... */ }
to_derived& some_function_two(ti_derived &arg) { /* ... */ }
ti_derived ti; // Somehow initialized
to_derived correct; // Somehow determined
test test_object(ti, correct);
if (!test_object(&some_function_one)) {
cout << "1: " << test_object.result;
}
if (!test_object(&some_function_two)) {
cout << "2: " << test_object.result;
}
Я предполагаю, что один и тот же объект типа test
может вызываться многократно на многих test_function
с, поэтому его член result
должен быть указателем, а не ссылкой: я не могу переназначить ссылку .
Проблема в том, что код для operator()
неверен: левая часть не является ссылкой на test_output
, поэтому он статически приводится к точному классу test_output
, который является чисто виртуальным; однако я хочу, чтобы operator==
был динамически связан с оператором равенства для типа to_derived
. Таким образом, он попытается понизить expected
до базового типа test_output
и пожаловаться на то, что у меня нет такого оператора (и если бы я это сделал, он все равно был бы неправильным). Примечание: переключение порядка операндов приведет к тому, что operator==
будет равным to_derived
, но тогда компилятор будет жаловаться на неправильный тип второго аргумента.
Полагаю, я мог бы сделать test
шаблоном в зависимости от типов, скажем, template <typename I, typename O>
, заменив test_input
и test_output
в своем коде, но это не так хорошо, потому что он не может указать, что I
O
наследуют виртуальные функции, которые я хочу (именно поэтому у меня есть эти два класса в первую очередь).
Заманчиво, хотя и не совсем элегантно, хотеть перегрузить operator==
для типа test_output*
, но это не законно, не так ли? Я мог бы сделать только один из аргументов указателем, поскольку expected
может оставаться ссылочным типом, но опять же: неэлегантный. Это не то, как я написал бы код, если бы мне не нужно было result
переназначаться, и поэтому я не хочу писать код.
Если бы этого не было в классе, я мог бы просто определить новую ссылочную переменную каждый раз, когда я хочу сохранить новый result
, но у меня может быть только так много членов. Синтаксически, это звучит как ситуация, когда я бы хотел "указатель на ссылку на test_output
", но это также незаконно. (Либо это, либо что-то вроде оператора "rebind
" для ссылок.) test_output **
не годится, так как я хочу, в конечном счете, иметь возможность передавать объекты (базового) типа test_output
; если я делаю одну разыменование, я просто получаю test_output *
, но если я делаю два, то тип больше не является динамическим.
Так как мне это сделать? В частности, я хочу:
, чтобы связать эквивалент operator==(*result, expected)
с оператором равенства для динамических типов этих вещей;
, чтобы иметь возможность указать компилятору, что эти типы являются производными от test_output
;
для возможности переназначения result
.
Мне также интересно: допустимо ли в моем примере использование этих указателей на функции в качестве аргументов для test_object
? Их аргументы могут быть динамически типизированы, но я не знаю, означает ли это, что сам тип функции имеет автоматическое преобразование в тип функции, принимающий и возвращающий соответствующие базовые типы. Если нет, то как бы я указал функцию этой динамически типизированной подписи?