在早期检测习惯用法中使用void模板参数 [英] Use of void template argument in early detection idiom implementation
问题描述
在 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_type
或false_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...>;
最后,我们还可以仅检查"特征是否对某些提供的T
和Args
有效:
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_t
和is_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屋!