模拟概念和约束的推荐方法是什么
What is the recommended way to simulate concepts and constraints?
在引入概念和约束之前,有几种方法可以模拟这种编译时检查。以"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); } }
这种方法不适用于函数重载。
-
使用
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); } }
如果一个过于"聪明"的家伙手动指定了第三个参数呢?
-
在功能原型中使用
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); } }
有时在函数重载中也不起作用。
-
使用
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); } }
我以前看到过,我想不出有什么缺点。
-
以及许多其他变体。
哪些是首选或推荐的?优点和缺点是什么?感谢您的帮助。
这是一个复杂的主题,很难回答您的问题。
不管怎样,一些意见/建议,没有任何详尽的借口。
(1)static_assert()
路
static_assert(LessThanComparable<U,T>, "oh this is not epic");
如果你想要一个只适用于某些类型的函数,并且如果用错误的类型调用时会给出错误(一个明显的错误,因为你可以选择错误消息),这是一个很好的解决方案。
但当你想要一个替代方案时,通常是错误的解决方案。这不是SFINAE解决方案。因此,用错误类型的参数调用函数会产生错误,并且不允许在替换中使用另一个函数。
(2) 你对的看法是正确的
typename = std::enable_if_t</* some test */>>
解决方案。用户可以手动显示第三个参数。开玩笑,我说这个解决方案可以被"劫持"。
但这并不是这个解决方案的唯一缺点。
假设您有两个互补的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 */ }
错误:这个解决方案根本不起作用,因为您启用/禁用的不是第二个typename,而是第二个type的默认值。因此,您并没有完全启用/禁用函数,编译器必须考虑具有相同签名的两个函数(函数的签名不取决于默认值);因此您发生了冲突并获得了错误。
以下解决方案,SFINAE上的返回类型
std::enable_if_t<LessThanComparable<U,T>, void> order(T& a, U& b)
(也没有void
,这是默认类型
std::enable_if_t<LessThanComparable<U,T>> order(T& a, U& b)
)或者在第二种类型上(遵循Yakk关于不标准允许的void *
的建议)
template <typename T, typename U,
std::enable_if_t<LessThanComparable<U,T>>, bool> = true>
是(IMHO)好的解决方案,因为它们都避免了劫持风险,并且与具有相同名称和签名的两个互补功能兼容。
我建议第三种可能的解决方案(不可劫持,互补兼容),即添加第三个启用/禁用SFINAE类型的默认值:
template <typename T, typename U>
void order(T& a, U& b, std::enable_if_t<LessThanComparable<U,T>> * = nullptr)
另一种可能的解决方案是完全避免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>{}); }
当条件为true时,LessThanComplarable
从std::true_type
继承,当条件为false时,从std::false_type
继承。
否则,如果LessThanComparable
只给出布尔值,则对order_helper()
的调用可以是
order_helper(a, b, std::integral_constant<bool, LessThanComparable<U,T>>{});
(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 ?
}
}
至少在某些版本的标准中不允许void*
类型的非类型模板参数;我会使用值为=true
的bool
。
否则,使用它。
您应该了解range-v3
库如何模拟概念https://github.com/ericniebler/range-v3/blob/master/include/range/v3/range_concepts.hpp
还有一种方法可以使用模板别名来实现类似于使用sfinae的别名模板的概念:语言允许吗?
而且,您在列表中错过了decltype
变体:
template <typename T, typename U>
auto order(T& a, U& b) -> decltype(void(b < a))
{
if (b < a)
{
using std::swap;
swap(a, b);
}
}
template <typename T, typename U,
typename = decltype(void(b < a))>
void order(T& a, U& b)
{
if (b < a)
{
using std::swap;
swap(a, b);
}
}
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 通过JNI传递数据数组的最快方法是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 在另一个类视图中添加最多2个图表的正确方法是什么
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 在C++中包含原型文件的正确方法是什么?
- 在 OpenCV C++ 中估计基本矩阵之前对相应点进行归一化的正确方法是什么?
- 在PostgreSQL中根据它们的ID选择大量行的最快方法是什么?
- 在OSX上使用CMake将Adobe的XMP工具包构建为共享库的最简单方法是什么?
- 将一系列整数放入类的最佳方法是什么?
- 从长整整转换为uint64_t的推荐方法是什么?
- 将此布尔值传递给此函数的最有效方法是什么?
- 通过比较C++中的行在 txt 文件中搜索的最简单方法是什么?