问题描述
在 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
首先对我没有任何意义.如果我尝试使用此类型特征来检测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
版本匹配.
// 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模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!