12

有一种(看似)不错的 C++03 方法可以找出类型是否具有成员函数或运算符:

https://github.com/jaredhoberock/is_call_possible/blob/master/is_call_possible.hpp

没有现代 C++11 方法可以做到这一点吗?最好不要包含任何外部代码而只使用标准。

4

4 回答 4

21

这适用于 GitHub 中给出的所有测试用例(演示:http: //ideone.com/ZLGp4R):

#include <type_traits>

template <typename C, typename F, typename = void>
struct is_call_possible : public std::false_type {};

template <typename C, typename R, typename... A>
struct is_call_possible<C, R(A...),
    typename std::enable_if<
        std::is_same<R, void>::value ||
        std::is_convertible<decltype(
            std::declval<C>().operator()(std::declval<A>()...)
            //                ^^^^^^^^^^ replace this with the member you need.
        ), R>::value
    >::type
> : public std::true_type {};
于 2013-06-19T21:50:43.523 回答
17

C++ 11 增加了一个新技巧,我经常开玩笑地称之为“CFINAE”(编译失败不是错误)。

它利用了decltype运算符和 SFINAE 的常规属性。

考虑以下函数:

template <typename X, typename Y>
static auto check(X& x, Y& y) -> decltype(x >> y);

X只有当并且Y是定义了移位运算符的类型时,才会在重载期间考虑它。添加一个常规的 catch-all 重载check,您就有了一种机制来测试是否可以编译任意表达式。

事实上,这是Andrew Sutton( Concepts Lite提案的作者之一)在实验性Origin库中开发的原则。事实上,我的示例是直接从这里实现的Streamable概念。

我推荐 Andrew Sutton 和 Bjarne Stroustrup 的 GoingNative 2012 演示文稿,他们介绍了新概念和 Origin 库:

http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/A-Concept-Design-for-C-

于 2013-06-20T13:49:37.910 回答
2

不,这几乎是一样的。或多或少。实现会有所不同,尽管您可以用标准库特征替换该实现内部使用的一些元函数。但是没有简单的方法来检测是否可以在给定一组参数的情况下调用某种类型的函数。

那是概念(PDF)

于 2013-06-19T21:19:20.187 回答
2

我使用以下基于 SFINAE 的众所周知的方法:

#define TYPE_SUPPORTS(ClassName, Expr)                         \
  template<typename U>                                         \
  struct ClassName                                             \
  {                                                            \
  private:                                                     \
    template<typename>                                         \
    static constexpr std::false_type test(...);                \
                                                               \
    template<typename T = U>                                   \
    static decltype((Expr), std::true_type{}) test(int) ;      \
                                                               \
  public:                                                      \
    static constexpr bool value = decltype(test<U>(0))::value; \
  };

宏的主要目的是简化添加类型检查。该宏定义了一个允许您对类型进行任意检查的结构T

例如,要检查std::begin()可以调用的类型:

namespace detail
{
  TYPE_SUPPORTS(SupportsBegin, std::begin(std::declval<T>()))
}

template<typename T>
constexpr bool supportsBegin()
{
   return detail::SupportsBegin<T>::value;
}

当然,detail命名空间和函数包装器都是语法糖,但在调用者方面稍微改进了语法。

于 2016-07-27T15:23:19.487 回答