推荐的模拟概念和约束的方法是什么?

L. *_* F. 5 c++ sfinae enable-if template-meta-programming c++11

在介绍概念和约束之前,有几种方法可以模拟此编译时检查。以“ order()”功能为例:(如何在LessThanComparable没有概念或约束的情况下实施是另一回事)

  • 使用 static_assert

    template <typename T, typename U>
    void order(T& a, U& b)
    {
        static_assert(LessThanComparable<U,T>, "oh this is not epic");
        if (b < a)
        {
            using std::swap;
            swap(a, b);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这种方法不适用于函数重载。

  • 使用 typename = enable_if

    template <typename T, typename U,
        typename = std::enable_if_t<LessThanComparable<U,T>>>>
    void order(T& a, U& b)
    {
        if (b < a)
        {
            using std::swap;
            swap(a, b);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    如果过分“聪明”的家伙用手指定了第三个参数怎么办?

  • 使用enable_if的函数原型:

    template <typename T, typename U>
    std::enable_if_t<LessThanComparable<U,T>>, void> order(T& a, U& b)
    {
        if (b < a)
        {
            using std::swap;
            swap(a, b);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    有时在函数重载中也不起作用。

  • 使用enable_if作为伪非类型模板参数的类型

    template <typename T, typename U,
        std::enable_if_t<LessThanComparable<U,T>>, void*> = nullptr> // or int = 0
    void order(T& a, U& b)
    {
        if (b < a)
        {
            using std::swap;
            swap(a, b);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    我以前看过这个,我想不到任何缺点。

  • 和许多其他变体。

哪个是更可取或推荐的?优点和缺点是什么?任何帮助表示赞赏。

max*_*x66 5

这是一个复杂的话题,要回答您的问题并不容易。

无论如何,有一些观察/建议,但并不假装是详尽无遗的。

(1)static_assert()方式

static_assert(LessThanComparable<U,T>, "oh this is not epic");
Run Code Online (Sandbox Code Playgroud)

如果您想要一个仅适用于某些类型的函数并且如果使用错误的类型调用时会给出错误(明确的错误,因为您可以选择错误消息),则是一个很好的解决方案。

但是,当您需要替代方案时,通常是错误的解决方案。不是SFINAE解决方案。因此,使用错误类型的参数调用该函数会产生错误,并且不允许在替换中使用另一个函数。

(2)您对

typename = std::enable_if_t</* some test */>>
Run Code Online (Sandbox Code Playgroud)

解。用户可以手动显示第三个参数。开玩笑,我说这个解决方案可以被“劫持”。

但这不是此解决方案的唯一缺点。

假设您必须foo()通过SFINAE启用/禁用两个互补功能;如果测试为真,则第一个;如果测试为假,则第二个。

您可以认为以下解决方案很危险(可以被劫持)但可以正常工作

/* true case */
template <typename T, typename = std::enable_if_t<true == some_test_v<T>>>
void foo (T t)
 { /* something with t */ }

/* false case */
template <typename T, typename = std::enable_if_t<false == some_test_v<T>>>
void foo (T t)
 { /* something with t */ }
Run Code Online (Sandbox Code Playgroud)

错误:此解决方案根本不起作用,因为您不是启用/禁用第二个类型名,而是仅启用/禁用第二个类型名的默认值。因此,您并没有完全启用/禁用这些功能,并且编译器必须考虑具有相同签名的两个功能(一个功能的签名不取决于默认值)。因此发生碰撞并获得错误。

以下解决方案,SFINAE超过返回的类型

std::enable_if_t<LessThanComparable<U,T>, void> order(T& a, U& b)
Run Code Online (Sandbox Code Playgroud)

(同样不带void,这是默认类型

std::enable_if_t<LessThanComparable<U,T>> order(T& a, U& b)
Run Code Online (Sandbox Code Playgroud)

)或第二种类型(遵循Yakk关于允许使用非标准的建议void *

template <typename T, typename U,
          std::enable_if_t<LessThanComparable<U,T>>, bool> = true> 
Run Code Online (Sandbox Code Playgroud)

是(IMHO)的好解决方案,因为它们都避免了劫持风险,并且与具有相同名称和签名的两个互补功能兼容。

我建议第三种可能的解决方案(不可劫持,互补兼容),即添加第三种具有SFINAE启用/禁用类型的默认值:

template <typename T, typename U>
void order(T& a, U& b, std::enable_if_t<LessThanComparable<U,T>> * = nullptr)
Run Code Online (Sandbox Code Playgroud)

另一种可能的解决方案是完全避免使用SFINAE,而是使用标签分派。像

template <typename T, typename U>
void order_helper (T & a, U & b, std::true_type const &)
 { if (b < a) { std::swap(a, b); } }

// something different if LessThanComparable is false ?
template <typename T, typename U>
void order_helper (T & a, U & b, std::false_type const &)
 { /* ???? */ }

template <typename T, typename U>
void order (T & a, U & b)
 { order_helper(a, b, LessThanComparable<U,T>{}); }
Run Code Online (Sandbox Code Playgroud)

这种情况是LessThanComplarablestd::true_type条件为真时继承的,是从std::false_type条件为假时继承的。

否则,如果LessThanComparable仅给出布尔值,则对的调用order_helper()可以为

order_helper(a, b, std::integral_constant<bool, LessThanComparable<U,T>>{});
Run Code Online (Sandbox Code Playgroud)

(3)如果可以使用C ++ 17,则有一种if constexpr方法可以避免很多重载

template <typename T, typename U>
void order(T& a, U& b)
{
   if constexpr ( LessThanComparable<U, T> )
    { 
      if ( b < a )
         std::swap(a, b);
    }
   else
    {
      // what else ?
    }
}
Run Code Online (Sandbox Code Playgroud)