经过几个小时的玩耍和一些C++聊天室里认真讨论 https://chat.stackoverflow.com/transcript/message/2392901#2392901,我们终于得到了一个适用于可能重载或继承的函子的版本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...>{};
Ideone 上的实例 http://ideone.com/Q20BZ。请注意,两个失败的测试都是超载的operator()
测试。这是一个带有可变参数模板的 GCC 错误,已在 GCC 4.7 中修复。 Clang 3.1 还将所有测试报告为passed
.
如果你想operator()
如果默认参数失败,有一种可能的方法可以做到这一点,但是其他一些测试将在那时开始失败,我发现尝试纠正它太麻烦了。
Edit:正如@Johannes 在评论中正确指出的那样,我们在这里遇到了一些不一致的地方,即定义函数指针转换的函子 https://stackoverflow.com/q/8873048/500104不会被检测为“可调用”。恕我直言,修复这个问题并不简单,因此我不会为此烦恼(暂时)。如果您绝对需要这个特性,那么请发表评论,我会看看我能做什么。
既然这一切都已经说了,IMHO,这个特质的想法是愚蠢的。为什么你会有这样的exact要求?为什么要制定标准is_callable
还不够吗?
(是的,我认为这个想法很愚蠢。是的,我仍然去建造了这个。是的,这很有趣,非常有趣。不,我没有疯。至少我是这么认为的......)