Почему в этом случае не удается вывести аргумент шаблона c ++?

Я пытаюсь написать свою собственную делегатскую систему в качестве замены для boost :: functions, так как последний выполняет много распределений кучи, которые я профилировал как проблемные.
Я написал это как замену (упрощенно, фактическая вещь использует объединенную память и размещение нового, но это достаточно просто, чтобы воспроизвести ошибку):

template<class A, class B>
struct DelegateFunctor : public MyFunctor {
DelegateFunctor(void (*fptr)(A, B), A arg1, B arg2) : fp(fptr), a1(arg1), a2(arg2) {}

virtual void operator()() { fp(a1, a2); }

void (*fp)(A, B);  // Stores the function pointer.
const A a1; const B a2;  // Stores the arguments.
};

и эта вспомогательная функция:

template<class A, class B>
MyFunctor* makeFunctor(void (*f)(A,B), A arg1, B arg2) {
return new DelegateFunctor<A,B>(f, arg1, arg2);
}

Странная вещь происходит здесь:

void bar1(int a, int b) {
// do something
}

void bar2(int& a, const int& b) {
// do domething
}

int main() {
int a = 0;
int b = 1;

// A: Desired syntax and compiles.
MyFunctor* df1 = makeFunctor(&bar1, 1, 2);

// B: Desired syntax but does not compile:
MyFunctor* df2 = makeFunctor(&bar2, a, b);

// C: Not even this:
MyFunctor* df3 = makeFunctor(&bar2, (int&)a, (const int&)b);

// D: Compiles but I have to specify the whole damn thing:
MyFunctor* df4 = makeFunctor<int&, const int&>(&bar2, a, b);
}

Ошибка компилятора, которую я получаю для версии C (B похожа):

error: no matching function for call to ‘makeFunctor(void (*)(int&, const int&), int&, const int&)’

что странно, потому что компилятор в своем сообщении об ошибке фактически правильно вывел типы.

Есть ли способ получить версию B для компиляции? Как boost :: bind обходит это ограничение?
Я использую GCC 4.2.1. Нет C ++ 11 решений, пожалуйста.

4

Решение

Аргументация выводит ссылки. Путем сопоставления подписи указателя функции для Aмы хотим получить int &, но сопоставляя фактический аргумент, мы хотим intи, следовательно, вычет не удается.

Одно из решений состоит в том, чтобы сделать второй тип не выводимым, например, так:

#include <type_traits>

template <typename R, typename A, typename B>
R do_it(R (*func)(A, B),
typename std::common_type<A>::type a,   // not deduced
typename std::common_type<B>::type b)   // not deduced
{
return func(a, b);
}

Сейчас A а также B определяются исключительно сигнатурой указателя функции:

int foo(int &, int const &);

int main()
{
int a = 0, b = 0;
return do_it(foo, a, b);  // deduces A = int &, B = int const &
}

(Обратите внимание, что std::common_type<T>::type является рекомендуемой идиомой для «типа идентичности», единственной целью которого является удаление аргумента шаблона из вывода аргумента. Ранее это называлось такими вещами, как identity<T>::type или же alias<T>, но стандартная черта библиотеки std::common_type служит этой цели просто отлично.)

5

Другие решения

При выводе аргументов шаблона с использованием аргумента значения вы всегда будете получать только тип значения. То есть, когда вы используете шаблон функции, такой как

template <typename T>
void f(T) {
}

тип T всегда будет не ссылочным типом. Теперь, когда вы пытаетесь передать указатель на функцию и значение, компилятор не может сделать выводимые типы согласованными, если функция не принимает тип значения:

template <typename T>
void f(void (*)(T), T) {}

void f0(int);
void f1(int const&);

int main() {
f(&f0, 0); // OK
f(&f1, 0); // ERROR
}

Одним из способов решения этой проблемы является соответствующая перегрузка соответствующего шаблона функции. Если вы добавите нижеприведенную функцию к миксу, приведенный выше пример снова будет работать:

template <typename T>
void f(void (*)(T const&), T const&) {}

Очевидно, что это быстро становится кошмаром обслуживания и, вероятно, не что ты хочешь делать. Альтернатива — использовать разные параметры шаблона для соответствующих аргументов:

template <typename T, typename S>
void f(void (*)(T), S) {}

Хотя это работает, это сразу же сказывается на том, что вы не обязательно соответствует типу, который вы действительно хотите сопоставить для второго аргумента: это будет тип значения, даже если вы захотите получить ссылочный тип (лично я сомневаюсь, что вы делаю но это уже другая проблема). Если вы не хотите, чтобы это происходило, вы можете запретить форме компилятора пытаться определить аргументы для некоторого аргумента. Например:

template <typename T>
struct helper {
typedef T type;
};
template <typename T>
void f(void (*)(T), typename helper<T>::type) {}

Хотя приведенный выше пример демонстрирует проблему, используя только один аргумент шаблона, я уверен, что это работает и с большим количеством аргументов шаблона. Является ли это тем, что повышает, я не знаю и не волнуюсь.

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector