如何在检测习语中要求精确的函数签名?
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< std::vector<int>, int >;
抛出类似no viable overloaded operator[]
的错误,因为std::vector
的下标运算符的签名是
std::vector<T, std::allocator<T>>::operator[](size_type pos);
GCC 中的size_type
是unsigned long
。如何避免从int
到size_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_or
、detected_t
、is_detected
。
带有模板参数typename Default
、template <typename...> Op
和typename ... Args
:
is_detected
:std::true_type
或std::false_type
的别名,取决于Op<Args...>
的有效性
detected_t
:Op<Args...>
或 nonesuch
的别名,取决于 Op<Args...>
的有效性。
detected_or
:结构的别名,value_t
是 is_detected
,type
是 Op<Args...>
或 Default
,取决于 Op<Args...>
的有效性
可以使用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");
;
【讨论】:
以上是关于如何在检测习语中要求精确的函数签名?的主要内容,如果未能解决你的问题,请参考以下文章