推荐的模拟概念和约束的方法是什么? [英] What is the recommended way to simulate concepts and constraints?
问题描述
在引入概念和约束之前,有几种方法可以模拟此编译时检查.以" order()
"功能为例:(如何在没有概念或约束的情况下实现 LessThanComparable
是另一回事)
Before the introduction of concepts and constraints, there are several ways to simulate this compile-time check. Take a "order()
" function for example: (how to implement LessThanComparable
without concepts or constraints is another story)
-
使用
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);
}
}
此方法不适用于函数重载.
This approach won't work for function overloading.
使用 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);
}
}
如果聪明"的家伙用手指定了第三个参数怎么办?
What if an over-"intelligent" guy specifies a third parameter by hand?
在函数原型中使用 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);
}
}
有时在函数重载中也不起作用.
Sometimes also doesn't work in function overloading.
使用 enable_if
作为虚拟非类型模板参数的类型
Use enable_if
as the type of a dummy non-type template parameter
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);
}
}
我以前看过这个,我想不到任何缺点.
I saw this before, and I can't think of any drawbacks.
和许多其他变体.
哪个是更可取或推荐的?优点和缺点是什么?感谢您的帮助.
Which ones are preferable or recommended? What is the benefits and drawbacks? Any help is appreciated.
推荐答案
这是一个复杂的主题,要回答您的问题并不容易.
It's a complicated topic and is not easy give an answer at your question.
无论如何,有些观察/建议,却没有任何详尽的伪装.
Anyway, some observations/suggestions, without any pretense to being exhaustive.
(1) static_assert()
方式
static_assert(LessThanComparable<U,T>, "oh this is not epic");
如果希望使用仅适用于某些类型的函数并且如果使用错误的类型进行调用会给出错误(明确的错误,因为您可以选择错误消息),则
是一个很好的解决方案.
is a good solution if you want a function that works only with some types and give an error (a clear error, because you can choose the error message) if called with wrong types.
但是,当您需要替代方案时,通常是错误的解决方案.不是SFINAE解决方案.因此,使用错误类型的参数调用该函数会产生错误,并且不允许在替换中使用另一个函数.
But usually is the wrong solution when you want an alternative. Isn't a SFINAE solution. So calling the function with arguments of wrong types gives an error and doesn't permit that another function is used in substitution.
(2)您对
typename = std::enable_if_t</* some test */>>
解决方案.用户可以手动显示第三个参数.开玩笑,我说这个解决方案可以被劫持".
solution. An user can explicit a third parameter by hand. Joking, I say that this solution can be "hijacked".
但这不是该解决方案的唯一缺点.
But isn't the only drawback of this solution.
假设您有两个互补的 foo()
函数,这些函数必须通过SFINAE启用/禁用;第一个测试为true时,第二个为相同测试为false时.
Suppose you have two complementary foo()
functions that have to be enabled/disabled via SFINAE; the first one when a test is true, the second one when the same test if false.
您可以认为以下解决方案很危险(可以被劫持)但可以正常工作
You can think that the following solution is dangerous (can be hijacked) but can work
/* 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 */ }
错误:此解决方案根本不起作用,因为您不是在启用/禁用第二种类型名,而是仅启用/禁用第二种类型名的默认值.因此,您并没有完全启用/禁用这些功能,并且编译器必须考虑具有相同签名的两个功能(一个功能的签名不取决于默认值).这样您就会发生碰撞并获得错误.
Wrong: this solution simply doesn't works because you're enabling/disabling not the second typename but only the default value for the second typename. So you're not completely enabling/disabling the functions and the compiler have to consider two functions with the same signature (the signature of a function doesn't depends from default values); so you have a collision and obtain an error.
以下解决方案,对返回的类型进行SFINAE
The following solutions, SFINAE over the returned type
std::enable_if_t<LessThanComparable<U,T>, void> order(T& a, U& b)
(也没有 void
,这是默认类型
(also without void
, that is the default type
std::enable_if_t<LessThanComparable<U,T>> order(T& a, U& b)
)或第二种类型(遵循Yakk关于非标准允许的 void *
的建议)
) or over the second type (following the Yakk's suggestion about a not-standard allowed void *
)
template <typename T, typename U,
std::enable_if_t<LessThanComparable<U,T>>, bool> = true>
是(IMHO)很好的解决方案,因为它们都避免了劫持风险,并且与具有相同名称和签名的两个互补功能兼容.
are (IMHO) good solutions because both of they avoid the hijacking risk and are compatible with two complementary functions with the same name and signature.
我建议第三种可能的解决方案(不可劫持,互补兼容),即添加第三种具有SFINAE启用/禁用类型的默认值:
I suggest a third possible solution (no hijack-able, complementary compatible) that is the addition of a third defaulted value with SFINAE enabled/disabled type: something as
template <typename T, typename U>
void order(T& a, U& b, std::enable_if_t<LessThanComparable<U,T>> * = nullptr)
另一种可能的解决方案是完全避免SFINAE,而是使用标签分派;像
Another possible solution avoid at all SFINAE but use tag-dispatching; something as
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>{}); }
如果条件为true,则 LessThanComplarable
继承自 std :: true_type
,如果条件为false则继承自 std :: false_type
This in case LessThanComplarable
inherit from std::true_type
when the condition is true, from std::false_type
when the condition is false.
否则,如果 LessThanComparable
仅提供布尔值,则对 order_helper()
的调用可以为
Otherwise, if LessThanComparable
gives only a boolean value, the call to order_helper()
can be
order_helper(a, b, std::integral_constant<bool, LessThanComparable<U,T>>{});
(3)如果可以使用C ++ 17,则有 if constexpr
方式可以避免很多重载
(3) if you can use C++17, there is the if constexpr
way that can avoid a lot of overloading
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 ?
}
}
这篇关于推荐的模拟概念和约束的方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!