测试是否可以使用元编程调用f(x)

olp*_*lpa 5 c++ template-meta-programming c++11

Stroustrup的书提供了一个如何回答这个问题的例子:" f(x)如果x是类型的话,是否可以调用X"(第28.4.4节"使用Enable_if的更多示例").我试图重现这个例子,但是出了点问题,无法理解.

在我的下面的代码中,有一个函数f(int).我希望那时的结果has_f<int>::value1(true).实际结果是0(false).

#include <type_traits>
#include <iostream>

//
// Meta if/then/else specialization
//
struct substitution_failure { };

template<typename T>
struct substitution_succeeded : std::true_type { };

template<>
struct substitution_succeeded<substitution_failure> : std::false_type { };

//
// sfinae to derive the specialization
//
template<typename T>
struct get_f_result {
private:
  template<typename X>
    static auto check(X const& x) -> decltype(f(x));
  static substitution_failure check(...);
public:
  using type = decltype(check(std::declval<T>()));
};

//
// has_f uses the derived specialization
//
template<typename T>
struct has_f : substitution_succeeded<typename get_f_result<T>::type> { };

//
// We will check if this function call be called,
// once with "char*" and once with "int".
//
int f(int i) {
  std::cout << i;
  return i;
}

int main() {
  auto b1{has_f<char*>::value};
  std::cout << "test(char*) gives: " << b1 << std::endl;
  std::cout << "Just to make sure we can call f(int): ";
  f(777);
  std::cout << std::endl;
  auto b2{has_f<int>::value};
  std::cout << "test(int) gives: " << b2 << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

输出:

test(char*) gives: 0
Just to make sure we can call f(int): 777
test(int) gives: 0
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

主要问题是你在f这里打了一个不合格的电话:

template<typename X>
static auto check(X const& x) -> decltype(f(x));
Run Code Online (Sandbox Code Playgroud)

f将要找到的s将是check()(none)定义范围内的那些,以及通过相关命名空间中的参数依赖查找找到的那些X.由于XIS int,它有没有相关的命名空间,你会发现没有f在那儿任.由于ADL永远不会起作用int,因此必须在get_f_result定义之前显示您的函数.只需将其向上移动就可以解决这个问题.


现在,你has_f过于复杂了.substitution_succeeded机器没有理由.只需让两个check()重载返回你想要的类型:

template<typename T>
struct has_f {
private:
    template <typename X>
    static auto check(X const& x)
        -> decltype(f(x), std::true_type{});

    static std::false_type check(...);
public:
  using type = decltype(check(std::declval<T>()));
};
Run Code Online (Sandbox Code Playgroud)

现在has_f<T>::type已经是true_type或者false_type.


当然,即使这样也过于复杂.检查表达式是否有效是一个相当常见的操作,因此简化它是有帮助的(从Yakk借来,类似于std::is_detected):

namespace impl {
    template <template <class...> class, class, class... >
    struct can_apply : std::false_type { };

    template <template <class...> class Z, class... Ts>
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type { };
};

template <template <class... > class Z, class... Ts>
using can_apply = impl::can_apply<Z, void, Ts...>;
Run Code Online (Sandbox Code Playgroud)

这是你写的:

template <class T>
using result_of_f = decltype(f(std::declval<T>()));

template <class T>
using has_f = can_apply<result_of_f, T>;    
Run Code Online (Sandbox Code Playgroud)