¿Por qué std :: function not equality equality comparable?
Creo que la razón principal es que, si así fuera, no se puede usar con tipos no equivalentes de igualdad, incluso si la comparación de igualdad nunca se realiza.
I.e. el código que realiza la comparación debe ser instanciado anticipadamente, en el momento en que el objeto invocable se almacena en std :: function, por ejemplo, en uno de los constructores o operadores de asignación.
Dicha limitación reduciría en gran medida el ámbito de aplicación, y obviamente no es aceptable para "contenedor de función polimórfica de propósito general".
Es improtante tener en cuenta, que es posible compare boost::function con objeto invocable (pero no con otro impulso :: función)
envoltorios de objetos de función se pueden comparar a través == o! = contra cualquier objeto de función que pueda almacenarse dentro del contenedor.
Esto es posible, ya que la función que realiza esta comparación se instantiniated en el punto de comparación, sobre la base de conocimientos tipo de operando.
Por otra parte, std :: function tiene target template member function, que se puede utilizar para realizar una comparación similar. De hecho, los operadores de comparación de boost :: function son implemented in terms of target member function.
Por lo tanto, no hay barreras técnicas que bloqueen la implementación de function_comparable.
Entre las respuestas no es común "imposible en general" patrón:
-
Incluso entonces, se obtendría un concepto estrecho de la igualdad, como funciones equivalentes compararían desigual si (por ejemplo) fueron construidos por argumentos vinculantes en un orden diferente. Creo que es imposible probar la equivalencia en el caso general.
-
puedo estar equivocado, pero creo que la igualdad es de std :: objetos función no es solucionable por desgracia en el sentido genérico.
-
Debido a la equivalencia de las máquinas de Turing es indecidible.Dado dos objetos funcionales diferentes, no es posible determinar si calculan la misma función o no. [Se suprimió Esa respuesta]
estoy completamente en desacuerdo con esto: no es el trabajo de std :: función para realizar la comparación en sí, es el trabajo es sólo para redirigir solicitud a comparación con los objetos subyacentes - eso es todo.
Si el tipo de objeto subyacente no define la comparación, será un error de compilación en cualquier caso, std :: function no es necesario para deducir el algoritmo de comparación.
Si el tipo de objeto subyacente define la comparación, pero que funciona mal, o tiene una semántica inusual, tampoco es un problema de la función std ::, pero es un problema de subyacente tipo.
Es posible implementar function_comparable basado en std :: función.
Aquí está la prueba de concepto:
template<typename Callback,typename Function> inline
bool func_compare(const Function &lhs,const Function &rhs)
{
typedef typename conditional
<
is_function<Callback>::value,
typename add_pointer<Callback>::type,
Callback
>::type request_type;
if (const request_type *lhs_internal = lhs.template target<request_type>())
if (const request_type *rhs_internal = rhs.template target<request_type>())
return *rhs_internal == *lhs_internal;
return false;
}
#if USE_VARIADIC_TEMPLATES
#define FUNC_SIG_TYPES typename ...Args
#define FUNC_SIG_TYPES_PASS Args...
#else
#define FUNC_SIG_TYPES typename function_signature
#define FUNC_SIG_TYPES_PASS function_signature
#endif
template<FUNC_SIG_TYPES>
struct function_comparable: function<FUNC_SIG_TYPES_PASS>
{
typedef function<FUNC_SIG_TYPES_PASS> Function;
bool (*type_holder)(const Function &,const Function &);
public:
function_comparable() {}
template<typename Func> function_comparable(Func f)
: Function(f), type_holder(func_compare<Func,Function>)
{
}
template<typename Func> function_comparable &operator=(Func f)
{
Function::operator=(f);
type_holder=func_compare<Func,Function>;
return *this;
}
friend bool operator==(const Function &lhs,const function_comparable &rhs)
{
return rhs.type_holder(lhs,rhs);
}
friend bool operator==(const function_comparable &lhs,const Function &rhs)
{
return rhs==lhs;
}
friend void swap(function_comparable &lhs,function_comparable &rhs)// noexcept
{
lhs.swap(rhs);
lhs.type_holder.swap(rhs.type_holder);
}
};
Existe cierta propiedad agradable - function_comparable puede ser comparado contra std :: función también.
Por ejemplo, supongamos que tenemos vector of std::function's, y queremos dar para el usuario register_callback y unregister_callback funciones. El uso de function_comparable se requiere sólo para parámetro unregister_callback:
void register_callback(std::function<function_signature> callback);
void unregister_callback(function_comparable<function_signature> callback);
Live demo at Ideone
El código fuente de demostración:
// Copyright Evgeny Panasyuk 2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <type_traits>
#include <functional>
#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <typeinfo>
#include <utility>
#include <ostream>
#include <vector>
#include <string>
using namespace std;
// _____________________________Implementation__________________________________________
#define USE_VARIADIC_TEMPLATES 0
template<typename Callback,typename Function> inline
bool func_compare(const Function &lhs,const Function &rhs)
{
typedef typename conditional
<
is_function<Callback>::value,
typename add_pointer<Callback>::type,
Callback
>::type request_type;
if (const request_type *lhs_internal = lhs.template target<request_type>())
if (const request_type *rhs_internal = rhs.template target<request_type>())
return *rhs_internal == *lhs_internal;
return false;
}
#if USE_VARIADIC_TEMPLATES
#define FUNC_SIG_TYPES typename ...Args
#define FUNC_SIG_TYPES_PASS Args...
#else
#define FUNC_SIG_TYPES typename function_signature
#define FUNC_SIG_TYPES_PASS function_signature
#endif
template<FUNC_SIG_TYPES>
struct function_comparable: function<FUNC_SIG_TYPES_PASS>
{
typedef function<FUNC_SIG_TYPES_PASS> Function;
bool (*type_holder)(const Function &,const Function &);
public:
function_comparable() {}
template<typename Func> function_comparable(Func f)
: Function(f), type_holder(func_compare<Func,Function>)
{
}
template<typename Func> function_comparable &operator=(Func f)
{
Function::operator=(f);
type_holder=func_compare<Func,Function>;
return *this;
}
friend bool operator==(const Function &lhs,const function_comparable &rhs)
{
return rhs.type_holder(lhs,rhs);
}
friend bool operator==(const function_comparable &lhs,const Function &rhs)
{
return rhs==lhs;
}
// ...
friend void swap(function_comparable &lhs,function_comparable &rhs)// noexcept
{
lhs.swap(rhs);
lhs.type_holder.swap(rhs.type_holder);
}
};
// ________________________________Example______________________________________________
typedef void (function_signature)();
void func1()
{
cout << "func1" << endl;
}
void func3()
{
cout << "func3" << endl;
}
class func2
{
int data;
public:
explicit func2(int n) : data(n) {}
friend bool operator==(const func2 &lhs,const func2 &rhs)
{
return lhs.data==rhs.data;
}
void operator()()
{
cout << "func2, data=" << data << endl;
}
};
struct Caller
{
template<typename Func>
void operator()(Func f)
{
f();
}
};
class Callbacks
{
vector<function<function_signature>> v;
public:
void register_callback_comparator(function_comparable<function_signature> callback)
{
v.push_back(callback);
}
void register_callback(function<function_signature> callback)
{
v.push_back(callback);
}
void unregister_callback(function_comparable<function_signature> callback)
{
auto it=find(v.begin(),v.end(),callback);
if(it!=v.end())
v.erase(it);
else
throw runtime_error("not found");
}
void call_all()
{
for_each(v.begin(),v.end(),Caller());
cout << string(16,'_') << endl;
}
};
int main()
{
Callbacks cb;
function_comparable<function_signature> f;
f=func1;
cb.register_callback_comparator(f);
cb.register_callback(func2(1));
cb.register_callback(func2(2));
cb.register_callback(func3);
cb.call_all();
cb.unregister_callback(func2(2));
cb.call_all();
cb.unregister_callback(func1);
cb.call_all();
}
de salida es:
func1
func2, data=1
func2, data=2
func3
________________
func1
func2, data=1
func3
________________
func2, data=1
func3
________________
P.S. Parece que con la ayuda de std::type_index es posible implementar una clase similar a function_comparable, que también admite pedidos (es decir, menos) o incluso hash. Pero no solo ordenando entre diferentes tipos, sino también ordenando dentro del mismo tipo (esto requiere soporte de tipos, como LessThanComparable).
Tenga en cuenta que usted puede pedir '* a.target < ftor_type >() == * b.target < ftor_type >()' si apuntan a la igualdad de funtores-comparables. Aunque esto es un poco meticuloso (el objeto subyacente no se convertirá implícitamente al tipo solicitado), sí especifica exactamente qué semántica de comparación se está utilizando. – Potatoswatter