可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
There are a number of similar questions/answers, but I couldn't quite put those answers together to serve my purposes. I want a traits
template<typename Func, typename ReturnType, typename... Args>
struct returns_a { static const bool value; };
such that
returns_a<F,T,Args>::value
is true if F(Args)
is well formed and returns a T
. After some more research, I got it working as follows:
// value is true if Func(Args...) is well formed
template<typename Func, typename... Args>
class is_callable
{
template <typename F>
static decltype(std::declval<F>()(std::declval<Args>()...), void(), 0) test(int);
template <typename>
static void test(...);
public:
static const bool value = !std::is_void<decltype(test<Func>(0))>::value;
};
// return_type<FunctionSignature>::type is the return type of the Function
template<typename>
struct return_type {};
template<typename ReturnType, typename... Args>
struct return_type<ReturnType(Args...)>
{ typedef ReturnType type; };
// helper class, required to use SFINAE together with variadic templates parameter
// generic case: Func(Args...) is not well-defined
template <typename Func, typename ReturnType, typename dummy, typename... Args>
struct returns_a_helper { static const bool value = false; };
// Func is a function signature
template <typename Func, typename ReturnType, typename... Args>
struct returns_a_helper<Func, ReturnType, typename
std::enable_if<std::is_function<Func>::value>::type, Args...>
{
static const bool value =
std::is_convertible<typename return_type<Func>::type,
ReturnType>::value;
};
// Func(Args...) not a function call, but well-defined
template <typename Func, typename ReturnType, typename... Args>
struct returns_a_helper<Func,ReturnType,typename
std::enable_if<is_callable<Func>::value &&
!std::is_function<Func>::value
>::type, Args...>
{
static const bool value =
std::is_convertible<typename std::result_of<Func(Args...)>::type,
ReturnType>::value;
};
template <typename Func, typename ReturnType, typename... Args>
struct returns_a : returns_a_helper<Func, ReturnType, void, Args...> {};
which now works fine for functors and functions. Here is a simple test:
struct base { virtual bool member(int) const = 0; };
struct foo : base { bool member(int x) const { return x&2; } };
struct bar { foo operator()() { return foo(); } };
foo free_function() { return foo(); }
template<typename T, typename Func>
void test(Func const&func)
{
std::cout << std::boolalpha << returns_a<Func,T>::value << std::endl;
}
int main()
{
foo x;
bar m;
test<const base&>([&]() { return x; });
test<const base&>(m);
test<const base&>(free_function);
return 0;
}
Well, this works, but it seems a bit cumbersome. Anybody has better/more elegant/shorter solutions?
回答1:
I think this will do:-
(Corrected including aschepler's test case)
#include <type_traits>
template<typename Func, typename Ret, typename... Args>
struct returns_a
{
template<typename F, typename ...As>
static constexpr bool test(
decltype(std::declval<F>()(std::declval<As>()...)) * prt) {
return std::is_same<Ret *,decltype(prt)>::value;
}
template <typename F, typename ...As>
static constexpr bool test(...) {
return false;
}
static const bool value = test<Func,Args...>(static_cast<Ret *>(0));
};
// Testing...
#include <iostream>
void fn0();
int fn1(int);
int fn2(int,int);
struct cls{};
struct fntor
{
int operator()(int i) { return 1; }
};
auto lamb0 = [](int i) -> int { return i; };
struct B {};
struct D : public B {};
auto lamb1 = []{ return B{}; };
int main()
{
std::cout << returns_a<decltype(fn0),void>::value << std::endl; // 1
std::cout << returns_a<decltype(fn1),int,int>::value << std::endl; // 1
std::cout << returns_a<decltype(fn1),int,double>::value << std::endl; // 1
std::cout << returns_a<decltype(fn1),double,int>::value << std::endl; // 0
std::cout << returns_a<decltype(fn1),char,int>::value << std::endl; // 0
std::cout << returns_a<decltype(fn1),unsigned,int>::value << std::endl; // 0
std::cout << returns_a<decltype(fn2),int,int,int>::value << std::endl; // 1
std::cout << returns_a<decltype(fn2),int,char,float>::value << std::endl; // 1
std::cout << returns_a<cls,int,int>::value << std::endl; // 0
std::cout << returns_a<fntor,int,int>::value << std::endl; // 1
std::cout << returns_a<decltype(lamb0),int,int>::value << std::endl; // 1
std::cout << returns_a<double,int,int>::value << std::endl; // 0
std::cout << returns_a<decltype(lamb1), D>::value << std::endl; //0
return 0;
}
(Built with clang 3.2, gcc 4.7.2, gcc 4.8.1)
回答2:
#include <tuple>
#include <utility>
template<typename Func, typename R, typename Args, typename=void>
struct will_return_helper: std::false_type {};
template<typename Func, typename R, typename... Args>
struct will_return_helper<
Func, R, std::tuple<Args...>,
typename std::enable_if<
std::is_same<
R,
decltype( std::declval<Func&>()( std::declval<Args>()... ) )
>::value
>::type
> : std::true_type {};
template<typename Func, typename R, typename... Args>
struct will_return:
will_return_helper< typename std::decay<Func>::type, R, std::tuple<Args...> >
{};
#include <iostream>
struct Foo {
int operator()(double) {return 0;}
};
int main()
{
std::cout << "1 = "<< will_return< int(), int >::value << "\n";
std::cout << "1 = "<< will_return< int(*)(), int >::value << "\n";
std::cout << "0 = "<< will_return< int(*)(), double >::value << "\n";
std::cout << "1 = "<< will_return< Foo, int, double >::value << "\n";
std::cout << "1 = "<< will_return< Foo, int, int >::value << "\n";
std::cout << "0 = "<< will_return< Foo, double, int >::value << "\n";
}
Live example.
A better signature for will_return
, in my opinion, would be:
template<typename Func, typename Sig>
struct will_return;
template<typename Func, typename R, typename... Args>
struct will_return<Func, R(Args...)>:
will_return_helper< typename std::decay<Func>::type, R, std::tuple<Args...> >
{};
which gives you:
std::cout << "1 = "<< will_return< int(), int() >::value << "\n";
and
std::cout << "1 = "<< will_return< Foo, int(double) >::value << "\n";
which I think looks prettier.
If you prefer "can be converted" rather than "is the same type", you can change is_same
above to is_convertible
.
回答3:
Well, this is embarrassing: I found a rather simple (hence elegant) way:
template <typename Func, typename ReturnType, typename... Args>
using returns_a = std::is_convertible<Func, std::function<ReturnType(Args...)>>;
when all the hard work is done in std::function<>
.