【问题标题】:Template overload based on condition基于条件的模板重载
【发布时间】:2021-01-01 01:04:01
【问题描述】:

使用类型特征,我可以执行以下操作:

template<typename Rect> Rect& move(Rect& rc, size_type<Rect> delta)
{
    rc.left += delta.width;
    rc.right += delta.width;
    rc.top += delta.height;
    rc.bottom += delta.height;
    return rc;
}
template<typename Rect> Rect& move(Rect& rc, point_type<Rect> to)
{
    int w = w(rc);
    int h = h(rc);
    rc.left = to.x;
    rc.top = to.y;
    rc.right = rc.left + w;
    rc.bottom = rc.top + h;
    return rc;
}

但是如何在不更改函数名称的情况下允许传递任何大小和点类型?显然我不能这样做:

template<typename Rect, typename Size> Rect& move(Rect& rc, Size delta);
template<typename Rect, typename Point> Rect& move(Rect& rc, Point to);

我想做的是

template<typename Rect, typename Size /*if Size::width, use this*/> Rect& move(Rect& rc, Size size);
template<typename Rect, typename Point /*if Point::x, use this*/> Rect& move(Rect& rc, Point to);

即选择重载取决于模板参数是否具有特定成员。用c++可以吗?

【问题讨论】:

    标签: c++ templates overloading metaprogramming template-meta-programming


    【解决方案1】:

    我想做的是

    template<typename Rect, typename Size /*if Size::width, use this*/>
    Rect& move(Rect& rc, Size size);
    
    template<typename Rect, typename Point /*if Point::x, use this*/> 
    Rect& move(Rect& rc, Point to);
    

    即选择重载取决于模板参数是否具有特定成员。用c++可以吗?

    如果您至少可以使用 C++11...您是否尝试过通过尾随返回类型和 decltype() 使用 SFINAE?

    我的意思是……像

    template <typename Rect, typename Size>
    auto move (Rect & rc, Size size)
       -> decltype( size.width, rc );
    // .............^^^^^^^^^^^  <-- note this
    
    template <typename Rect, typename Point> 
    auto move(Rect& rc, Point to)
       -> decltype( to.x, rc );
    // .............^^^^^  <-- and note this
    

    如果您调用 move() 并使用带有 withx 成员的第二个参数,这显然不起作用:编译器不知道 move() 选择哪一个。

    它是如何工作的?

    很简单:主要是SFINAE,意思是替换失败不是错误。

    考虑到decltype() 返回所包含表达式的类型,因此(例如)来自

       decltype( size.width, rc );
    

    逗号操作符丢弃size.widthif available(这是重点!),保留rc,所以decltype()返回类型rc(如果存在size.width!)。

    但是如果size.width 不存在怎么办?

    你有一个“替换失败”。这“不是错误”,而是从可用的move() 函数集中删除move() 的重载版本。

    【讨论】:

    • 有趣,这行得通。不过,遗憾的是,我现在很难理解编译器在这种情况下是如何工作的。
    • 我可以将它与类型特征混合使用吗?像这样template&lt;typename Rect&gt; auto convert(Rect rc) -&gt; decltype(rc.left, convert_type&lt;Rect&gt;{}/*rvalue reference, not what I want*/)
    • @olekstolar - 是的,你可以混合使用:decltype() 只返回表达式的类型;如果类型是从类型特征获得的,则并不重要。与此同时,我改进了答案,试图解释它是如何工作的。
    • 阅读后我发现decltype 为右值表达式返回 T,为左值返回 T&,为 xvalue 返回 T&&。 T{} 绝对是一个右值。还有“逗号表达式结果的类型、值、值类别正好是第二个操作数的类型、值、值类别”——来自cppreference。
    • @olekstolar - 计算一下我使用了decltype( t ) 而不是decltype( T{} )decltype( t )T &amp;。无论如何,您也可以在decltype() 之后添加&amp;decltype( T{} ) &amp;。顺便说一句:尽可能避免decltype( T{} );改用decltype( std::declval&lt;T&gt;() )(即T &amp;);所以当T 不是默认可构造时它也可以工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    • 1970-01-01
    • 2016-10-04
    相关资源
    最近更新 更多