如何在检测习语中要求精确的函数签名?

Posted

技术标签:

【中文标题】如何在检测习语中要求精确的函数签名?【英文标题】:How to require an exact function signature in the detection idiom? 【发布时间】:2017-12-28 05:35:31 【问题描述】:

假设我有一个类型T,我想检测它是否有一个下标运算符,我可以用另一个类型Index 调用它。以下示例运行良好:

#include <type_traits>
#include <vector>

template < typename T, typename Index >
using subscript_t = decltype(std::declval<T>()[std::declval<Index>()]);

int main()

    using a = subscript_t< std::vector<int>, size_t >;
    using b = subscript_t< std::vector<int>, int    >;

然而,我希望当且仅当函数签名完全匹配时才能检测到该函数。在上面的示例中,我希望语句subscript_t&lt; std::vector&lt;int&gt;, int &gt;; 抛出类似no viable overloaded operator[] 的错误,因为std::vector 的下标运算符的签名是

std::vector<T, std::allocator<T>>::operator[](size_type pos);

GCC 中的size_typeunsigned long。如何避免从intsize_t 的隐式转换发生?

【问题讨论】:

【参考方案1】:

使用is_detected,您可以这样做:

template <typename T, typename Ret, typename Index>
using subscript_t = std::integral_constant<Ret (T::*) (Index), & T::operator[]>;


template <typename T, typename Ret, typename Index>
using has_subscript = is_detected<subscript_t, T, Ret, Index>;

static_assert(has_subscript<std::vector<int>, int&, std::size_t>::value, "!");
static_assert(!has_subscript<std::vector<int>, int&, int>::value, "!");

Demo


我在 SO 文档中写了这个:

is_detected

泛化type_trait创建:基于SFINAE 有实验性特征detected_ordetected_tis_detected

带有模板参数typename Defaulttemplate &lt;typename...&gt; Optypename ... Args

is_detectedstd::true_typestd::false_type 的别名,取决于Op&lt;Args...&gt; 的有效性 detected_tOp&lt;Args...&gt;nonesuch 的别名,取决于 Op&lt;Args...&gt; 的有效性。 detected_or:结构的别名,value_tis_detectedtypeOp&lt;Args...&gt;Default,取决于 Op&lt;Args...&gt; 的有效性

可以使用std::void_t 为 SFINAE 实现如下:

namespace detail 
    template <class Default, class AlwaysVoid,
              template<class...> class Op, class... Args>
    struct detector
    
        using value_t = std::false_type;
        using type = Default;
    ;

    template <class Default, template<class...> class Op, class... Args>
    struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
    
        using value_t = std::true_type;
        using type = Op<Args...>;
    ;

 // namespace detail

// special type to indicate detection failure
struct nonesuch 
    nonesuch() = delete;
    ~nonesuch() = delete;
    nonesuch(nonesuch const&) = delete;
    void operator=(nonesuch const&) = delete;
;

template <template<class...> class Op, class... Args>
using is_detected =
    typename detail::detector<nonesuch, void, Op, Args...>::value_t;

template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;

template <class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;

然后可以简单地实现检测方法存在的特征:

template <typename T, typename ...Ts>
using foo_type = decltype(std::declval<T>().foo(std::declval<Ts>()...));

struct C1 ;

struct C2 
    int foo(char) const;
;

template <typename T>
using has_foo_char = is_detected<foo_type, T, char>;

static_assert(!has_foo_char<C1>::value, "Unexpected");
static_assert(has_foo_char<C2>::value, "Unexpected");

static_assert(std::is_same<int, detected_t<foo_type, C2, char>>::value,
              "Unexpected");

static_assert(std::is_same<void, // Default
                           detected_or<void, foo_type, C1, char>>::value,
              "Unexpected");
static_assert(std::is_same<int, detected_or<void, foo_type, C2, char>>::value,
              "Unexpected");

【讨论】:

我会提到它不是 C++11/14 的一部分。 它也不是 C++17 的一部分;它还没有被合并。它可能会也可能不会进入 C++2a。【参考方案2】:

你可以这样做:

template <typename Index, typename ClassType, typename ReturnType>
constexpr bool arg_t(ReturnType (ClassType::*)(Index))

    return true;


template <typename T, typename Index>
struct subscript_t

    static_assert(arg_t<Index>(&T::operator[]));
;

请注意,您需要实例化 subscript_t,而不仅仅是命名其类型:

int main()

    subscript_t< std::vector<int>, size_t > a;
    subscript_t< std::vector<int>, int    > b;

另一种方法,可让您确定确切的错误消息:

template <typename ClassType, typename ReturnType, typename ArgType>
constexpr ArgType arg_t(ReturnType (ClassType::*)(ArgType))

    return ;


template <typename T, typename Index>
struct subscript_t

    using Actual = decltype(arg_t(&T::operator[]));
    static_assert(std::is_same<Index, Actual>::value, "oops");
;

【讨论】:

以上是关于如何在检测习语中要求精确的函数签名?的主要内容,如果未能解决你的问题,请参考以下文章

从完全相同的@Configuration类习语中注入@Beans

如何有效地提高瞬断线束测试仪的精确度和灵敏度?

Java中的命名参数习语

如何检测一个棋盘角?

如何在windows开发环境下得到精确到微秒的系统时间?

如何在 Python 中实现常见的 bash 习语? [关闭]