首页 > 解决方案 > 检查元组中的项目是否包含特定方法

问题描述

我有一个元组,我正在尝试为元组中的每个对象调用initialize()。现在,某些对象可能不包含该功能。我知道我可以通过使用 C++20要求功能来检查类是否包含给定函数来完成这项工作。不幸的是,我正在使用 MSVC,但它还不支持。

有可能写得不一样吗?我可以使用 C++20 但它应该在 MSVC 上编译

template <class... Args>
struct CustomTuple : std::tuple<Args...> {
  // Calls "initialize() for each item inside the tuple if that function exists"
  template <std::size_t I = 0>
  auto initialize() {
       std::decay_t<decltype(std::get<I>(*this))> item;
       constexpr bool has_initialize = requires() { // doesnt't work on MSVC
           item.initialize(); 
       };
       if constexpr (has_initialize) {
           std::get<I>(*this).initialize();
       }
       if constexpr (I + 1 != sizeof...(Args)) {
           initialize<I + 1>();
    }
  }
};

标签: c++tuples

解决方案


如果您还没有访问权限requires,那么您将不得不使用 SFINAE 并编写一个类型特征来检测是否initialize()是可调用的成员函数。

这可以用 来完成std::void_t,尽管有点尴尬:

#include <type_traits> // std::void_t
#include <utility>     // std::declval

template <typename T, typename = void>
struct has_initialize_impl : std::false_type {};

template <typename T>
struct has_initialize_impl<T, 
  std::void_t<decltype(std::declval<T&>().initialize())>
> : std::true_type {};

template <typename T>
inline constexpr auto has_initialize = has_initialize_impl<T>::value;

上述特征检查是否有任何可变引用T&可以调用名为initialize(). 如果表达式格式正确,则has_initialized计算结果为true; 否则,它的计算结果为false

然后可以将其与您的其余代码一起使用,只需稍作更改:

template <class... Args>
struct CustomTuple : std::tuple<Args...> {
  // Calls "initialize() for each item inside the tuple if that function exists"
  template <std::size_t I = 0>
  auto initialize() {
       // Note: no need to construct a temporary/unused 'item' type,
       // we can just use 'tuple_element' here to get the type
       using element_type = typename std::tuple_element<I, std::tuple<Args...>>::type;
       
       if constexpr (has_initialize<element_type >) {
           std::get<I>(*this).initialize();
       }
       if constexpr (I + 1 != sizeof...(Args)) {
           initialize<I + 1>();
    }
  }
};

Live Example


如果您认为您可能有更多需要检测功能的情况,但仍不requires支持 C++20,那么可能值得考虑将“检测到”-idiom + 样板代码添加到您的代码中,这可以在cppreference上找到。

这与实际行为相同std::void_t,但如果经常需要会更方便。这仍然比 c++20's 更迟钝requires,但如果你的编译器不支持它,你就没有太多选择了。

使用is_detected_v,您只需为需要验证的任何表达式定义模板类型别名,然后将其简单地传递给特征。

例如,上述解决方案可以简洁地解决为:

template <typename T>
using detect_initialize = decltype(std::declval<T&>().initialize());

...

if constexpr (is_detected_v<detect_initialize, decltype(item)>) {
    ...
}

Live Example


推荐阅读