有没有办法将std::function
转换为T*
参数转换为具有void*
参数的类似参数?似乎可能因为调用应该在二进制级别兼容。
例如,如何在不将Producer
转换为模板或丢失类型安全的情况下完成此工作?
#include <functional>
struct Producer {
// produces an int from a callable and an address
template<class Src>
Producer(Src& src, std::function<int (Src*)> f)
: arg_(&src),
f_(f)
{}
int operator()() {
return f_(arg_);
}
// type erasure through void* but still type safe since ctor
// checks that *arg_ and f are consistent
void* arg_;
std::function<int (void*)> f_;
};
int func1(char* c) {
return *c;
}
int func2(int* i) {
return *i;
}
int try_it() {
char c = 'a';
char i = 5;
// we want to make these work
Producer p1(c, func1);
Producer p2(i, func2);
// but we still want this to fail to compile
// Producer p3(c, func2);
return p1() + p2();
}
编辑:Solution具有明确的UDB但行为正确。 : - /
答案 0 :(得分:3)
您可以不将参数视为std::function
,然后您不必担心它。选择任何类型F
,只要您可以使用Src*
调用它,它就会返回可以转换为int
的内容。从SFINAE友好std::result_of_t
开始(从Yakk无耻地借来):
template<class F, class...Args>
using invoke_result = decltype( std::declval<F>()(std::declval<Args>()...));
并将其用于SFINAE你的构造函数:
template<class Src,
class F,
class = std::enable_if_t<
std::is_convertible<invoke_result<F, Src*>, int>::value
>>
Producer(Src& src, F f)
: arg_(&src)
, f_([f = std::move(f)](void* arg){
return f(static_cast<Src*>(arg));
})
{ }
没有UB那里。这也正确地拒绝了您的p3
案例。此外,除非您出于其他原因使用它,否则您甚至不需要arg_
。将f_
存储为:
std::function<int ()> f_;
并将Src
粘贴在其中:
template<class Src,
class F,
class = std::enable_if_t<
std::is_convertible<invoke_result<F, Src*>, int>::value
>>
Producer(Src& src, F f)
: f_([&src, f = std::move(f)](){
return f(&src);
})
{ }