kev*_*man 10 c++ templates sfinae template-meta-programming c++11
我想编写一个C++元函数is_callable<F, Arg>,定义value为true,当且仅当类型F具有表单的函数调用运算符时SomeReturnType operator()(const Arg &).例如,在以下情况中
struct foo {
void operator(const int &) {}
};
Run Code Online (Sandbox Code Playgroud)
我想is_callable<foo, int &>成为false和is_callable<foo, const int &>成为true.这是我到目前为止:
#include <memory>
#include <iostream>
template<typename F, typename Arg>
struct is_callable {
private:
template<typename>
static char (&test(...))[2];
template<unsigned>
struct helper {
typedef void *type;
};
template<typename UVisitor>
static char test(
typename helper<
sizeof(std::declval<UVisitor>()(std::declval<Arg>()), 0)
>::type
);
public:
static const bool value = (sizeof(test<F>(0)) == sizeof(char));
};
struct foo {
void operator()(const int &) {}
};
using namespace std;
int main(void)
{
cout << is_callable<foo, int &>::value << "\n";
cout << is_callable<foo, const int &>::value << "\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这版画1和1,但我想0和1,因为foo只定义void operator()(const int &).
在C++聊天室玩了几个小时并进行了一些认真的讨论后,我们终于得到了一个版本,适用于可能重载或继承的函子operator()和函数指针,基于@ KerrekSB和@ BenVoigt的版本.
#include <utility>
#include <type_traits>
template <typename F, typename... Args>
class Callable{
static int tester[1];
typedef char yes;
typedef yes (&no)[2];
template <typename G, typename... Brgs, typename C>
static typename std::enable_if<!std::is_same<G,C>::value, char>::type
sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (C::*pfn)(Brgs...));
template <typename G, typename... Brgs, typename C>
static typename std::enable_if<!std::is_same<G,C>::value, char>::type
sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (C::*pfn)(Brgs...) const);
template <typename G, typename... Brgs>
static char sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (G::*pfn)(Brgs...));
template <typename G, typename... Brgs>
static char sfinae(decltype(std::declval<G>()(std::declval<Brgs>()...)) (G::*pfn)(Brgs...) const);
template <typename G, typename... Brgs>
static yes test(int (&a)[sizeof(sfinae<G,Brgs...>(&G::operator()))]);
template <typename G, typename... Brgs>
static no test(...);
public:
static bool const value = sizeof(test<F, Args...>(tester)) == sizeof(yes);
};
template<class R, class... Args>
struct Helper{ R operator()(Args...); };
template<typename R, typename... FArgs, typename... Args>
class Callable<R(*)(FArgs...), Args...>
: public Callable<Helper<R, FArgs...>, Args...>{};
Run Code Online (Sandbox Code Playgroud)
Ideone上的实例.请注意,两个失败的测试是重载operator()测试.这是具有可变参数模板的GCC错误,已在GCC 4.7中修复.Clang 3.1还将所有测试报告为passed.
如果你想让operator()默认参数失败,有一种可能的方法可以做到这一点,但是其他一些测试会在那时开始失败,我觉得尝试纠正它太麻烦了.
编辑:正如@Johannes在评论中正确注意到的那样,我们在这里有点不一致,即定义转换为函数指针的仿函数不会被检测为"可调用".这是非常非常重要的修复,因此我不会打扰它(现在).如果你绝对需要这个特性,那么,发表评论,我会看到我能做些什么.
既然已经说过这一切,恕我直言,这个特性的想法是愚蠢的.为什么你有这样的确切要求?为什么标准is_callable不够?
(是的,我认为这个想法是愚蠢的.是的,我仍然去构建这个.是的,它很有趣,非常如此.不,我不是疯了.至少我相信......)
(向Kerrek道歉,以他的答案为出发点)
编辑:更新以处理类型,没有任何operator().
#include <utility>
template <typename F, typename Arg>
struct Callable
{
private:
static int tester[1];
typedef char yes;
typedef struct { char array[2]; } no;
template <typename G, typename Brg>
static char sfinae(decltype(std::declval<G>()(std::declval<Brg>())) (G::*pfn)(Brg)) { return 0; }
template <typename G, typename Brg>
static char sfinae(decltype(std::declval<G>()(std::declval<Brg>())) (G::*pfn)(Brg) const) { return 0; }
template <typename G, typename Brg>
static yes test(int (&a)[sizeof(sfinae<G,Brg>(&G::operator()))]);
template <typename G, typename Brg>
static no test(...);
public:
static bool const value = sizeof(test<F, Arg>(tester)) == sizeof(yes);
};
struct Foo
{
int operator()(int &) { return 1; }
};
struct Bar
{
int operator()(int const &) { return 2; }
};
struct Wazz
{
int operator()(int const &) const { return 3; }
};
struct Frob
{
int operator()(int &) { return 4; }
int operator()(int const &) const { return 5; }
};
struct Blip
{
template<typename T>
int operator()(T) { return 6; }
};
struct Boom
{
};
struct Zap
{
int operator()(int) { return 42; }
};
#include <iostream>
int main()
{
std::cout << "Foo(const int &): " << Callable<Foo, int const &>::value << std::endl
<< "Foo(int &): " << Callable<Foo, int &>::value << std::endl
<< "Bar(const int &): " << Callable<Bar, const int &>::value << std::endl
<< "Bar(int &): " << Callable<Bar, int &>::value << std::endl
<< "Zap(const int &): " << Callable<Zap , const int &>::value << std::endl
<< "Zap(int&): " << Callable<Zap , int &>::value << std::endl
<< "Wazz(const int &): " << Callable<Wazz, const int &>::value << std::endl
<< "Wazz(int &): " << Callable<Wazz, int &>::value << std::endl
<< "Frob(const int &): " << Callable<Frob, const int &>::value << std::endl
<< "Frob(int &): " << Callable<Frob, int &>::value << std::endl
<< "Blip(const int &): " << Callable<Blip, const int &>::value << std::endl
<< "Blip(int &): " << Callable<Blip, int &>::value << std::endl
<< "Boom(const int &): " << Callable<Boom, const int &>::value << std::endl
<< "Boom(int&): " << Callable<Boom, int &>::value << std::endl;
}
Run Code Online (Sandbox Code Playgroud)