在早期检测习惯用法中使用void模板参数 [英] Use of void template argument in early detection idiom implementation

查看:108
本文介绍了在早期检测习惯用法中使用void模板参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

n4502 中作者描述了封装void_t技巧的检测习惯用法的早期实现.这是它的定义以及为is_assignable(实际上是is_copy_assignable)定义特征的用法

In n4502 the authors describe an early implementation of the detect idiom that encapsulates the void_t trick. Here's its definition along with usage for defining a trait for is_assignable (really it's is_copy_assignable)

template<class...>
using void_t = void;

// primary template handles all types not supporting the operation:
template< class, template<class> class, class = void_t< > >
struct
detect : std::false_type { };
// specialization recognizes/validates only types supporting the archetype:
template< class T, template<class> class Op >
struct
detect< T, Op, void_t<Op<T>> > : std::true_type { };

// archetypal expression for assignment operation:
template< class T >
using
assign_t = decltype( std::declval<T&>() = std::declval<T const &>() );

// trait corresponding to that archetype:
template< class T >
using
is_assignable = detect<void, assign_t, T>;

他们提到,由于is_assignable特性使用的void,他们不喜欢这样:

They mention that they don't like this because of the void used in the is_assignable trait:

尽管生成的代码比 原来,我们不喜欢上面的检测接口,因为void 元函数调用中的参数是一个实现细节, 不应泄露给客户端代码.

Although the resulting code was significantly more comprehensible than the original, we disliked the above detect interface because the void argument in the metafunction call is an implementation detail that shouldn’t leak out to client code.

但是,void首先对我没有任何意义.如果我尝试使用此类型特征来检测int是否可复制分配,则会得到std::false_type 演示.

However, the void doesn't make any sense to me in the first place. If I try to use this type trait to detect if int is copy assignable, I get std::false_type Demo.

如果我将is_assignable重写为:

template< class T >
using
is_assignable = detect<T, assign_t>;

这对我来说更有意义,那么该特征似乎可以正常工作: 演示

Which makes more sense to me, then the trait appears to work correctly: Demo

所以我的问题是我误解了本文档中的某些内容,还是仅仅是拼写错误?

如果它是拼写错误,那么我不明白为什么作者觉得有必要讨论他们不喜欢void泄漏的原因,这使我很确定自己只是缺少一些东西.

If it was a typo, then I don't understand why the authors felt the need to discuss how they didn't like void leaking out, which makes me pretty sure I'm just missing something.

推荐答案

基于作者如何编写最终实现is_detected的判断,他们打算将Op用作可变参数模板,该模板可以表达更多概念:

Judging on how the authors wrote their final implementation of is_detected, they intended that Op be a variadic template, which allows one to express many more concepts:

(也从 n4502 )

// primary template handles all types not supporting the archetypal Op:
template< class Default
, class // always void; supplied externally
, template<class...> class Op
, class... Args
>
struct
detector
{
  using value_t = false_type;
  using type = Default;
};
// the specialization recognizes and handles only types supporting Op:
template< class Default
, template<class...> class Op
, class... Args
>
struct
detector<Default, void_t<Op<Args...>>, Op, Args...>
{
  using value_t = true_type;
  using type = Op<Args...>;
};
//...
template< template<class...> class Op, class... Args >
using
is_detected = typename detector<void, void, Op, Args...>::value_t;

进入这种情况时,必须使用void,以便当Op<Args...>是有效表达式时,模板专用化将与true_type版本匹配.

When you get into a scenario like this, a void becomes necessary so that template specialization will match the true_type version when Op<Args...> is a valid expression.

这是我对原始检测为可变参数的调整:

// primary template handles all types not supporting the operation:
template< class T, template<class...> class Trait, class... TraitArgs >
struct
detect : std::false_type { };
// specialization recognizes/validates only types supporting the archetype:
template< class T, template<class...> class Trait, class... TraitArgs >
struct
detect< T, Trait, std::void_t<Trait<T, TraitArgs...>>, TraitArgs... > : std::true_type { };

template<class T, template<class...> class Trait, class... TraitArgs>
using is_detected_t = typename detect<T, Trait, void, TraitArgs...>::type; 

template<class T, template<class...> class Trait, class... TraitArgs>
constexpr bool is_detected_v = detect<T, Trait, void, TraitArgs...>::value;

请注意,我将Op重命名为Trait,将Args重命名为TraitArgs,并使用 std::void_t 使其变成了C ++ 17.

Note that I renamed Op to Trait, Args to TraitArgs, and used std::void_t which made it into C++17.

现在让我们定义一个特征以测试名为Foo的功能,该功能可以接受也可以不接受某些参数类型:

Now let's define a trait to test for a function named Foo that can may or may not accept certain parameter types:

template<class T, class... Args>
using HasFoo_t = decltype( std::declval<T>().Foo(std::declval<Args>()...));

现在,只要给出一些T和我们的特征,我们就可以得到一个类型(true_typefalse_type):

Now we can get a type (true_type or false_type) given some T and our trait:

template< class T, class... Args>
using has_foo_t = is_detected_t<T, HasFoo_t, Args...>;

最后,我们还可以仅检查"特征是否对某些提供的TArgs有效:

And finally, we can also "just check" to see if the trait is valid for some provided T and Args:

template<class T, class... Args>
constexpr bool has_foo_v = is_detected_v<T, HasFoo_t, Args...>;

这是开始测试的结构:

struct A
{
    void Foo(int)
    {
        std::cout << "A::Foo(int)\n";
    }
};

最后是测试:

std::cout << std::boolalpha << has_foo_v<A, int> << std::endl; //true
std::cout << std::boolalpha << has_foo_v<A> << std::endl; // false

如果我从is_detected_tis_detected_v实现中删除了void,则选择了主要专业化,然后得到了false(

If I remove the void from my is_detected_t and is_detected_v implementations, then the primary specialization is chosen, and I get false (Example).

这是因为在其中void可以匹配std::void_t<Trait<T, TraitArgs...>>,如果模板参数格式正确,则您记得std::void_t<Trait<T, TraitArgs...>>的类型是void.如果template参数的格式不正确,则std::void_t<Trait<T, TraitArgs...>>不能很好地匹配,它将恢复为默认的特殊化(false_type).

This is because the void is there so as to match std::void_t<Trait<T, TraitArgs...>> which if you recall will have a type of void if the template argument is well-formed. If the template argument is not well-formed, then std::void_t<Trait<T, TraitArgs...>> is not a good match and it will revert to the default specialization (false_type).

当我们从调用中删除void时(只需将TraitArgs...留在原处),那么我们就无法匹配true_type专业化中的std::void_t<Trait<T, TraitArgs...>>参数.

When we remove void from our call (and simply leave TraitArgs... in its place) then we cannot match the std::void_t<Trait<T, TraitArgs...>> argument in the true_type specialization.

还要注意,如果std::void_t<Trait<T, TraitArgs...>>格式正确,它只是在主模板中的class... TraitArgs参数中提供了void类型,因此我们不需要定义额外的模板参数来接收.

Also note that if std::void_t<Trait<T, TraitArgs...>> is well-formed, it simply provides a void type to the class... TraitArgs argument in the primary template, so we don't need to define an extra template parameter to receive void.

最后,作者希望删除最终会出现在客户端代码中的void,因此,本文稍后将对它们进行更复杂的实现.

In conclusion, the authors wanted to remove the void that would end up in client code, hence their slightly more complicated implementation later in the paper.

感谢@Rerito指出此答案,Yakk还做了一些额外的工作来避免讨厌的事情void在客户端代码中.

Thanks to @Rerito for pointing out this answer where Yakk also puts in a little extra work to avoid the pesky void in client code.

这篇关于在早期检测习惯用法中使用void模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆