且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在早期检测习惯用法中使用void模板参数

更新时间: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_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.