更新时间:2023-11-30 22:11:28
基于作者如何编写最终实现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.