【问题标题】:Can we use the detection idiom to check if a class has a member function with a specific signature?我们可以使用检测习语来检查一个类是否具有具有特定签名的成员函数吗?
【发布时间】:2016-06-20 23:43:01
【问题描述】:

鉴于detection idiom 的(简化)实现

namespace type_traits
{
    template<typename... Ts>
    using void_t = void;

    namespace detail
    {
        template<typename, template<typename...> class, typename...>
        struct is_detected : std::false_type {};

        template<template<class...> class Operation, typename... Arguments>
        struct is_detected<void_t<Operation<Arguments...>>, Operation, Arguments...> : std::true_type {};
    }

    template<template<class...> class Operation, typename... Arguments>
    using is_detected = detail::is_detected<void_t<>, Operation, Arguments...>;

    template<template<class...> class Operation, typename... Arguments>
    constexpr bool is_detected_v = detail::is_detected<void_t<>, Operation, Arguments...>::value;
}

我们可以很容易地检查一个类foo是否包含一个成员函数bar

struct  foo {
    int const& bar(int&&) { return 0; }
};

template<class T>
using bar_t = decltype(std::declval<T>().bar(0));

int main()
{
    static_assert(type_traits::is_detected_v<bar_t, foo>, "not detected");
    return 0;
}

但是,如您所见,我们无法检测到foo::bar 的参数类型是int&amp;&amp;。检测成功,因为0 可以传递给foo::bar。我知道有很多选项可以检查(成员)函数的 exact 签名。但是我想知道,是否可以修改这个检测工具包以检测foo::bar的参数类型恰好是int&amp;&amp;

[我已经为这个例子创建了一个live demo。]

【问题讨论】:

  • 如果唯一未知的部分是返回类型,这很简单。您是否还想涵盖多个参数,而您只想精确指定一些参数?那么成员函数的 cv 和 ref 限定符呢?
  • @dyp 目前,is_detected_v&lt;bar_t, T&gt; 将检测T 是否有any 成员函数bar,它接受int。第一步,我想添加某种is_detected_exact 来检测T 是否有一个成员函数bar,其参数类型是exactly intexactly int&amp;&amp;exactly int const&amp; 等。在第二步中,我也想验证一个确切的返回类型,但我想这很容易,因为 bar_t 正是返回类型(只要decltype(...) 中的表达式格式正确)。
  • @dyp 是的,像这样!我们可以用functional 标头的某些实用程序替换bar_helper 吗?
  • 好吧,对于我的解决方案,您至少需要对返回类型进行类型推导。我不知道&lt;functional&gt; 中有任何此类工具。

标签: c++ templates c++14 sfinae typetraits


【解决方案1】:

在不改变你的 type_traits 的情况下,你可以这样做

template<typename T, T> struct helper {};

template<class T>
using bar_t = decltype(helper<const int& (T::*)(int&&), &T::bar>{});

Demo

【讨论】:

  • 哪里可以用helper替换integral_constant
【解决方案2】:

改编dypJarod42的思路,我想出了

template<class T, typename... Arguments>
using bar_t = std::conditional_t<
    true,
    decltype(std::declval<T>().bar(std::declval<Arguments>()...)),
    std::integral_constant<
        decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...),
        &T::bar
    >
>;

注意bar_t 将是bar 调用的返回类型。通过这种方式,我们与工具包保持一致。我们可以通过

static_assert(type_traits::is_detected_v<bar_t, foo, int&&>, "not detected");

然而,虽然这个解决方案完全符合我的预期,但我讨厌我需要为我想要检测的每种方法编写“如此复杂的代码”。我已经针对这个问题询问了new question

【讨论】:

  • 可以简化为template&lt;class T, typename... Arguments&gt; using bar_t = typename std::integral_constant&lt; decltype(std::declval&lt;T&gt;().bar(std::declval&lt;Arguments&gt;()...)) (T::*)(Arguments...), &amp;T::bar &gt;::value_type;
【解决方案3】:

我认为这不适用于检查 const 限定符。

decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...)

总是产生一个非常量函数指针类型,而如果bar 被标记为 const,&amp;T::bar 将产生一个 const 函数指针。

这将尝试将 const 指针类型转换为非常量指针类型以存储在 integral_constant 中。

【讨论】:

    猜你喜欢
    • 2015-08-03
    • 1970-01-01
    • 2017-06-15
    • 2014-12-09
    • 2022-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    相关资源
    最近更新 更多