Я пытался использовать следующий код C ++, но он не компилируется с ошибкой no matching function for call to ...
для вызова z
внутри bar::s
при компиляции с g ++ 4.9.2 и --std=c++11
:
template <class T>
class foo {
public:
virtual void s( T scale , const foo<T>& rhs ) {
}
};
template <class T>
class bar : public foo<T> {
public:
T z( T init , T (*f)( T a , T& x , const T& y ) , const foo<T>& rhs ) {
}
void s( T scale , const foo<T>& rhs ) {
this->z( ((T)0) , [=]( T a, T& l, const T& r ) {return ((l+=scale*r)?((T)0):((T)0));} , rhs );
}
};
int main () {
bar<float>* a = new bar<float>();
foo<float>* b = new foo<float>();
return 0;
}
Код компилируется, если я удаляю virtual
перед определением метода, а также если я удаляю захваченный scale
из лямбды. Если правильно понять проблему, она работает следующим образом: когда метод не является виртуальным, лямбда с захватом используется через operator ()
его связанного типа. Когда метод является виртуальным, этот оператор не может использоваться, потому что он не может быть вызван с помощью динамической диспетчеризации, только с помощью обычного вызова метода. Но лямбда также не может быть преобразована в указатель на функцию, потому что это преобразование доступно только для лямбд без захвата. И я предполагаю, что z
вызывается посредством динамической отправки, если s
является виртуальным, даже если z
не является виртуальным. Этот анализ правильный?
Я не понимаю, почему z
нельзя вызвать с помощью обычного вызова метода - в конце концов, он не виртуальный. Я также не понимаю, почему удаление параметров шаблона и замена всех T
s на float
s приводит к сбою компиляции как в виртуальном, так и в не виртуальном случае (удаление захваченного scale
по-прежнему позволяет выполнить успешную компиляцию ).
В любом случае, существует ли простой и несколько эффективный способ исправить это? Я хотел бы использовать лямбды с перехватами (или чем-то подобным) как в виртуальных, так и в не виртуальных методах шаблонных классов. Я знаю о std::function
, но он довольно тяжелый. А лямбды без захвата немного менее мощные, чем лямбды с захватами, поэтому они, вероятно, не смогут решить мои проблемы.
PS: Если вы хотите знать, что должен делать z
: это комбинация того, что будет zipWith
с последующим foldl
в Haskell (или что-то вроде mapreduce с двумя списками ввода, если вы не не знаю Haskell) и может использоваться для большинства унарных и двоичных операций с использованием foo
s. В моей первоначальной реализации init
не был типа T
, а вместо этого использовал в качестве типа дополнительный параметр шаблона, который был удален здесь для простоты.