计算函数指针的类型

Computing the type of a function pointer

本文关键字:类型 指针 函数 计算      更新时间:2023-10-16

考虑以下内容:

template<typename T>
struct S
{
    typedef M< &T::foo > MT;
}

这适用于:

S<Widget> SW;

其中Widget::foo()是某种函数

我如何修改struct S的定义以允许以下内容:

S<Widget*> SWP;

您需要的是以下类型转换。

  • 给定T,返回T
  • 给定T *,返回T

碰巧的是,标准库已经在std::remove_pointer中为我们实现了这一点(尽管自己做并不难)。

有了这个,你就可以写

using object_type = std::remove_pointer_t<T>;
using return_type = /* whatever foo returns */;
using MT = M<object_type, return_type, &object_type::foo>;

关于您的评论,即您也希望使用智能指针,我们必须重新定义类型转换。

  • 给定智能指针类型smart_ptr<T>,返回smart_ptr<T>::element_type,它应该是T
  • 给定一个指针类型T *,返回T
  • 否则,给定T,返回T本身

为此,我们必须编写自己的元函数。至少,我不知道标准库中有什么能对这里有所帮助。

我们首先定义主template("否则"的情况)。

template <typename T, typename = void>
struct unwrap_obect_type { using type = T; };

默认为void的第二个(匿名)类型参数稍后将使用。

对于(原始)指针,我们提供以下部分专门化。

template <typename T>
struct unwrap_obect_type<T *, void> { using type = T; };

如果我们停在这里,我们基本上会得到std::remove_pointer。但我们将为智能指针添加一个额外的部分专用化。当然,我们首先必须定义什么是"智能指针"。为了这个例子的目的,我们将把每个带有嵌套typedef、名为element_type的类型都视为智能指针。根据需要调整此定义。

template <typename T>
struct unwrap_obect_type
<
  T,
  std::conditional_t<false, typename T::element_type, void>
>
{
  using type = typename T::element_type;
};

第二类参数CCD_ 20是在C++14中模拟CCD_。我们的想法是,我们有下面的偏类型函数。

  • 给定一个类型T和一个名为element_type的嵌套typedef,返回void
  • 否则,触发替换失败

因此,如果我们处理智能指针,我们将获得比主template更好的匹配,否则,SFINAE将不再考虑这种部分专用化。

下面是一个工作示例。T.C.建议使用std::mem_fn来调用成员函数。这使得代码比我最初的示例干净得多。

#include <cstddef>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
template <typename ObjT, typename RetT, RetT (ObjT::*Pmf)() const noexcept>
struct M
{
  template <typename ThingT>
  static RetT
  call(ThingT&& thing) noexcept
  {
    auto wrapper = std::mem_fn(Pmf);
    return wrapper(std::forward<ThingT>(thing));
  }
};
template <typename T, typename = void>
struct unwrap_obect_type { using type = T; };
template <typename T>
struct unwrap_obect_type<T *, void> { using type = T; };
template <typename T>
struct unwrap_obect_type<T, std::conditional_t<false, typename T::element_type, void>> { using type = typename T::element_type; };
template <typename T>
struct S
{
  template <typename ThingT>
  void
  operator()(ThingT&& thing) const noexcept
  {
    using object_type = typename unwrap_obect_type<T>::type;
    using id_caller_type          = M<object_type, int,                &object_type::id>;
    using name_caller_type        = M<object_type, const std::string&, &object_type::name>;
    using name_length_caller_type = M<object_type, std::size_t,        &object_type::name_length>;
    std::cout << "id:          " << id_caller_type::call(thing)          << "n";
    std::cout << "name:        " << name_caller_type::call(thing)        << "n";
    std::cout << "name_length: " << name_length_caller_type::call(thing) << "n";
  }
};
class employee final
{
 private:
  int id_ {};
  std::string name_ {};
 public:
  employee(int id, std::string name) : id_ {id}, name_ {std::move(name)}
  {
  }
  int                  id()          const noexcept { return this->id_; }
  const std::string&   name()        const noexcept { return this->name_; }
  std::size_t          name_length() const noexcept { return this->name_.length(); }
};
int
main()
{
  const auto bob = std::make_shared<employee>(100, "Smart Bob");
  const auto s_object = S<employee> {};
  const auto s_pointer = S<employee *> {};
  const auto s_smart_pointer = S<std::shared_ptr<employee>> {};
  s_object(*bob);
  std::cout << "n";
  s_pointer(bob.get());
  std::cout << "n";
  s_smart_pointer(bob);
}