std :: function이 동등하지 않은 이유는 무엇입니까?
이 질문은 boost::function
및 에도 적용됩니다 std::tr1::function
.
std::function
동등하지 않음 :
#include <functional>
void foo() { }
int main() {
std::function<void()> f(foo), g(foo);
bool are_equal(f == g); // Error: f and g are not equality comparable
}
C ++ 11에서는 operator==
및 operator!=
오버로드가 존재하지 않습니다. 초기 C ++ 11 초안에서 오버로드는 주석 (N3092 §20.8.14.2)과 함께 삭제 된 것으로 선언되었습니다.
// deleted overloads close possible hole in the type system
"타입 시스템의 가능한 구멍"이 무엇인지 말하지 않습니다. TR1 및 Boost에서는 오버로드가 선언되지만 정의되지는 않습니다. TR1 사양 주석 (N1836 §3.7.2.6) :
이러한 멤버 함수는 정의되지 않은 채로 두어야합니다.
[ 주 : 부울 형 변환 기능, 두 인스턴스를 통해 비교 될 수있다 허점 열리고
==
이상!=
. 이러한 정의되지 않은void
연산자는 허점을 닫고 컴파일 시간 오류를 보장합니다. —end note ]
"허점"에 대한 나의 이해는 bool
변환 함수 가있는 경우 해당 변환이 동등 비교 (및 다른 상황에서)에 사용될 수 있다는 것입니다.
struct S {
operator bool() { return false; }
};
int main() {
S a, b;
bool are_equal(a == b); // Uses operator bool on a and b! Oh no!
}
나는 C ++ 03의 safe-bool 관용구와 C ++ 11의 명시 적 변환 함수를 사용하여이 "허점"을 피하는 데 사용되었다는 인상을 받았습니다. Boost와 TR1은 모두 safe-bool 관용구를 사용 function
하고 C ++ 11은 bool
변환 함수를 명시 적으로 만듭니다.
둘 다있는 클래스의 예로서, std::shared_ptr
둘 다 명시 적 bool
변환 기능이 있으며 동등 비교 가능합니다.
std::function
평등이 비교할 수없는 이유는 무엇 입니까? "타입 시스템의 가능한 구멍"은 무엇입니까? 와 어떻게 다른 std::shared_ptr
가요?
std::function
평등이 비교할 수없는 이유는 무엇 입니까?
std::function
는 임의의 호출 가능 유형에 대한 래퍼이므로 동등 비교를 구현하려면 모든 호출 가능 유형이 동등 비교 가능해야하고 함수 객체를 구현하는 모든 사람에게 부담을 주어야합니다. 그럼에도 불구하고 동등한 함수는 (예를 들어) 인수를 다른 순서로 바인딩하여 구성한 경우 같지 않은 함수를 비교하므로 좁은 평등 개념을 얻을 수 있습니다. 일반적인 경우 동등성을 테스트하는 것은 불가능하다고 생각합니다.
"타입 시스템의 가능한 구멍"은 무엇입니까?
이것은 연산자를 삭제하는 것이 더 쉽다는 것을 의미하며 이전에 발견되지 않은 일부 코너 케이스에서 원치 않는 암시 적 변환이 발생할 가능성이 없음을 증명하는 것보다 연산자를 사용하면 유효한 코드를 제공하지 않는다는 것을 확실히 알고 있습니다.
와 어떻게 다른
std::shared_ptr
가요?
std::shared_ptr
잘 정의 된 같음 의미 체계가 있습니다. 두 포인터는 둘 다 비어 있거나 비어 있지 않고 동일한 객체를 가리키는 경우에만 동일합니다.
이것은 Boost.Function FAQ 에서 자세히 논의됩니다 . :-)
내가 틀렸을 수도 있지만, std::function
불행히도 객체의 평등은 일반적인 의미에서 해결할 수 없다고 생각합니다 . 예를 들면 :
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <cstdio>
void f() {
printf("hello\n");
}
int main() {
boost::function<void()> f1 = f;
boost::function<void()> f2 = boost::bind(f);
f1();
f2();
}
이다 f1
와 f2
동일? 다양한 방식으로 서로를 감싸는 임의의 수의 함수 객체를 추가하면 결국 f
... 에 대한 호출로 귀결됩니다 .
std :: function이 동등하지 않은 이유는 무엇입니까?
주된 이유는 그것이 가능하다면 동등 비교가 수행되지 않더라도 동등하지 않은 비교 유형과 함께 사용할 수 없다는 것입니다.
즉, 비교를 수행하는 코드는 호출 가능한 객체가 생성자 또는 할당 연산자 중 하나에서 std :: function에 저장 될 때 초기에 인스턴스화되어야합니다.
이러한 제한은 적용 범위를 크게 좁히고 "범용 다형성 함수 래퍼" 에는 허용되지 않습니다 .
boost :: function 을 호출 가능한 객체와 비교할 수 있다는 점에 유의하는 것은 중요 하지 않습니다 (그러나 다른 boost :: function과는 아님).
함수 개체 래퍼는 == 또는! =를 통해 래퍼 내에 저장 될 수있는 모든 함수 개체와 비교할 수 있습니다.
이러한 비교를 수행하는 함수는 알고있는 피연산자 유형을 기반으로 비교 지점에서 인스턴스화되기 때문에 가능합니다.
또한 std :: function 에는 유사한 비교를 수행하는 데 사용할 수있는 대상 템플릿 멤버 function 이 있습니다. 사실 boost :: function의 비교 연산자는 대상 멤버 함수 측면에서 구현됩니다 .
따라서 function_comparable의 구현을 차단하는 기술적 장벽이 없습니다 .
답변 중에는 일반적인 "일반적으로 불가능한"패턴이 있습니다.
-
그럼에도 불구하고 동등한 함수는 (예를 들어) 인수를 다른 순서로 바인딩하여 구성한 경우 같지 않은 함수를 비교하므로 좁은 평등 개념을 얻을 수 있습니다. 일반적인 경우 동등성을 테스트하는 것은 불가능하다고 생각합니다.
-
내가 틀렸을 수도 있지만 평등은 std :: function 객체라고 생각합니다. 불행히도 일반적인 의미에서는 해결할 수 없습니다.
-
튜링 기계의 동등성은 결정할 수 없기 때문입니다. 두 개의 다른 functionobject가 주어지면 동일한 함수를 계산하는지 여부를 결정할 수 없습니다. [답변이 삭제되었습니다]
나는 이것에 완전히 동의하지 않는다 : 비교 자체를 수행하는 것은 std :: function의 일이 아니며, 기본 객체에 대한 비교 요청을 리디렉션 하는 것입니다. 그게 전부입니다.
기본 객체 유형이 비교를 정의하지 않으면 어떤 경우에도 컴파일 오류가 발생하며 비교 알고리즘을 추론하는 데 std :: function이 필요하지 않습니다.
기본 객체 유형이 비교를 정의하지만 잘못 작동하거나 비정상적인 의미가있는 경우 std :: function 자체의 문제는 아니지만 기본 유형의 문제입니다 .
std :: function을 기반으로 function_comparable 을 구현할 수 있습니다.
다음은 개념 증명입니다.
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);
}
};
멋진 건물이있다 - function_comparable가 비교 될 수있다 표준 : : 기능 너무.
예를 들어 std :: function 's의 벡터 가 있고 사용자 register_callback 및 unregister_callback 함수에 대해 제공하려고 한다고 가정 해 보겠습니다 . function_comparable의 사용은 unregister_callback 매개 변수 에만 필요합니다 .
void register_callback(std::function<function_signature> callback);
void unregister_callback(function_comparable<function_signature> callback);
데모 소스 코드 :
// 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();
}
출력은 다음과 같습니다.
func1
func2, data=1
func2, data=2
func3
________________
func1
func2, data=1
func3
________________
func2, data=1
func3
________________
추신 std :: type_index의 도움으로 ordering (ie less) 또는 해싱도 지원하는 function_comparable 클래스 와 유사한 구현이 가능합니다 . 그러나 서로 다른 유형 간의 주문뿐만 아니라 동일한 유형 내에서 주문할 수도 있습니다 (이렇게하려면 LessThanComparable과 같은 유형의 지원이 필요합니다).
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1240 에 따르면 :
The leading comment here is part of the history of
std::function
, which was introduced with N1402. During that time no explicit conversion functions existed, and the "safe-bool" idiom (based on pointers-to-member) was a popular technique. The only disadvantage of this idiom was that given two objects f1 and f2 of type std::function the expression
f1 == f2;
was well-formed, just because the built-in operator== for pointer to member was considered after a single user-defined conversion. To fix this, an overload set of undefined comparison functions was added, such that overload resolution would prefer those ending up in a linkage error. The new language facility of deleted functions provided a much better diagnostic mechanism to fix this issue.
In C++0x, the deleted functions are considered superfluous with the introduction of explicit conversion operators, so they will probably be removed for C++0x.
The central point of this issue is, that with the replacement of the safe-bool idiom by explicit conversion to bool the original "hole in the type system" does no longer exist and therefore the comment is wrong and the superfluous function definitions should be removed as well.
As for why you can't compare std::function
objects, it's probably because they can possibly hold global/static functions, member functions, functors, etc, and to do that std::function
"erases" some information about the underlying type. Implementing an equality operator would probably not be feasible because of that.
Actually, you can compare targets. It may work depends of what you want from comparison.
Here the code with inequality, but you can see how it works:
template <class Function>
struct Comparator
{
bool operator()(const Function& f1, const Function& f2) const
{
auto ptr1 = f1.target<Function>();
auto ptr2 = f2.target<Function>();
return ptr1 < ptr2;
}
};
typedef function<void(void)> Function;
set<Function, Comparator<Function>> setOfFunc;
void f11() {}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "was inserted - " << setOfFunc.insert(bind(&f11)).second << endl; // 1 - inserted
cout << "was inserted - " << setOfFunc.insert(f11).second << endl; // 0 - not inserted
cout << "# of deleted is " << setOfFunc.erase(f11) << endl;
return 0;
}
Ups, it is only valid since C++11.
How about trying something like the following, this works well for testing templates.
if (std::is_same<T1, T2>::value)
{
...
}
the least that could be done is if std::function saves the address of the function used for binding to a string and used string comparison instead.
참고URL : https://stackoverflow.com/questions/3629835/why-is-stdfunction-not-equality-comparable
'Development Tip' 카테고리의 다른 글
PropertyDefinition이 일치하지 않습니다. (0) | 2020.12.04 |
---|---|
Visual Studio에서 이전에 실행 된 몇 줄이 무엇인지 확인하기 위해 뒤로 물러 설 방법이 있습니까? (0) | 2020.12.04 |
추상 클래스 명명 규칙 (0) | 2020.12.04 |
LLDB 디버거에서 메서드를 호출하거나 코드를 실행하는 방법은 무엇입니까? (0) | 2020.12.04 |
.class : last-of-type이 작동하지 않는 이유는 무엇입니까? (0) | 2020.12.04 |