- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在花一些时间学习如何在 C++ 中使用模板。我从没用过之前,我并不总是确定在不同情况下可以实现什么或不能实现什么。
作为练习,我包装了我在事件中使用的一些 Blas 和 Lapack 函数,我目前正在包装 ?GELS
(计算线性方程组的解)。
A x + b = 0
?GELS
函数(仅适用于实数值)有两个名称:SGELS
, 对于单精度 vector 和 DGELS
double 。
我对界面的想法是一个函数solve
这样:
const std::size_t rows = /* number of rows for A */;
const std::size_t cols = /* number of cols for A */;
std::array< double, rows * cols > A = { /* values */ };
std::array< double, ??? > b = { /* values */ }; // ??? it can be either
// rows or cols. It depends on user
// problem, in general
// max( dim(x), dim(b) ) =
// max( cols, rows )
solve< double, rows, cols >(A, b);
// the solution x is stored in b, thus b
// must be "large" enough to accomodate x
根据用户要求,问题可能是多定的或未定的,这意味着:
dim(b) > dim(x)
(解是伪逆)dim(b) < dim(x)
(解决方案是 LSQ 最小化)dim(b) = dim(x)
(解是 A
的逆)(不考虑个别情况)。
自 ?GELS
将结果存储在输入 vector 中 b
, std::array
应该有足够的空间来容纳解决方案,如代码注释 (max(rows, cols)
) 中所述。
我想(编译时)确定采用哪种解决方案(这是一个参数更改在 ?GELS
称呼)。我有两个功能(为了这个问题我正在简化),处理精度并且已经知道哪个是 b
的维度和 rows
的数量/cols
:
namespace wrap {
template <std::size_t rows, std::size_t cols, std::size_t dimb>
void solve(std::array<float, rows * cols> & A, std::array<float, dimb> & b) {
SGELS(/* Called in the right way */);
}
template <std::size_t rows, std::size_t cols, std::size_t dimb>
void solve(std::array<double, rows * cols> & A, std::array<double, dimb> & b) {
DGELS(/* Called in the right way */);
}
}; /* namespace wrap */
是内部包装器的一部分。用户函数,确定所需的大小在b
通过模板 vector :
#include <type_traits>
/** This struct makes the max between rows and cols */
template < std::size_t rows, std::size_t cols >
struct biggest_dim {
static std::size_t const value = std::conditional< rows >= cols, std::integral_constant< std::size_t, rows >,
std::integral_constant< std::size_t, cols > >::type::value;
};
/** A type for the b array is selected using "biggest_dim" */
template < typename REAL_T, std::size_t rows, std::size_t cols >
using b_array_t = std::array< REAL_T, biggest_dim< rows, cols >::value >;
/** Here we have the function that allows only the call with b of
* the correct size to continue */
template < typename REAL_T, std::size_t rows, std::size_t cols >
void solve(std::array< REAL_T, cols * rows > & A, b_array_t< REAL_T, cols, rows > & b) {
static_assert(std::is_floating_point< REAL_T >::value, "Only float/double accepted");
wrap::solve< rows, cols, biggest_dim< rows, cols >::value >(A, b);
}
以这种方式它确实有效。但是我想更进一步,我真的不知道该怎么做。如果用户尝试调用 solve
与 b
如果尺寸太小,编译器会引发极其难以阅读的错误。
我正在尝试插入一个static_assert
这有助于用户理解他的错误。但我脑海中出现的任何方向需要使用两个具有相同签名的函数(这就像模板重载?)我找不到 SFINAE 策略(实际上它们根本不编译)。
您认为有可能针对错误的情况提出静态断言吗 b
在编译时不改变用户界面维度?我希望这个问题足够清楚。
@Caninonos:对我来说,用户界面就是用户调用求解器的方式,即:
solve< type, number of rows, number of cols > (matrix A, vector b)
这是我为了提高我的技能而对我的锻炼施加的约束。这意味着,我不知道是否真的有可能实现解决方案。 b
的类型必须匹配函数调用,如果我添加另一个模板参数并更改用户界面,这很容易违反我的约束。
这是一个最小的完整的工作示例。根据要求,我删除了对线性代数概念的任何引用。是个数的问题。这些案例是:
N1 = 2, N2 =2
.自 N3 = max(N1, N2) = 2
一切正常N1 = 2, N2 =1
.自 N3 = max(N1, N2) = N1 = 2
一切正常N1 = 1, N2 =2
.自 N3 = max(N1, N2) = N2 = 2
一切正常N1 = 1, N2 =2
.自 N3 = N1 = 1 < N2
它正确地引发了编译错误。我想用一个静态断言来拦截编译错误,该断言解释了 N3
的维度这一事实。是错的。至于现在,错误很难阅读和理解。最佳答案
首先是一些改进,可以稍微简化设计并提高可读性:
不需要 biggest_dim
. std::max
自 C++14 起为 constexpr。您应该改用它。
不需要 b_array_t
.你可以只写 std::array< REAL_T, std::max(N1, N2)>
现在来解决您的问题。 C++17 中的一种好方法是:
template < typename REAL_T, std::size_t N1, std::size_t N2, std::size_t N3>
void solve(std::array< REAL_T, N1 * N2 > & A, std::array< REAL_T, N3> & b) {
if constexpr (N3 == std::max(N1, N2))
wrap::internal< N1, N2, N3 >(A, b);
else
static_assert(N3 == std::max(N1, N2), "invalid 3rd dimension");
// don't write static_assert(false)
// this would make the program ill-formed (*)
}
或者,正如@max66 所指出的
template < typename REAL_T, std::size_t N1, std::size_t N2, std::size_t N3>
void solve(std::array< REAL_T, N1 * N2 > & A, std::array< REAL_T, N3> & b) {
static_assert(N3 == std::max(N1, N2), "invalid 3rd dimension");
if constexpr (N3 == std::max(N1, N2))
wrap::internal< N1, N2, N3 >(A, b);
}
Tadaa!! 简单、优雅、漂亮的错误消息。
constexpr if 版本与 static_assert
之间的区别即:
void solve(...)
{
static_assert(...);
wrap::internal(...);
}
是只有static_assert
吗?编译器将尝试实例化 wrap::internal
即使在 static_assert
失败,污染错误输出。如果调用 wrap::internal
,则使用 constexpr在条件失败时不是正文的一部分,因此错误输出是干净的。
(*) 我不写 static_asert(false, "error msg)
的原因是因为这会使程序格式错误,不需要诊断。参见 constexpr if and static_assert
您还可以制作 float
/double
如果你想通过在不可扣除的参数之后移动模板参数来扣除:
template < std::size_t N1, std::size_t N2, std::size_t N3, typename REAL_T>
void solve(std::array< REAL_T, N1 * N2 > & A, std::array< REAL_T, N3> & b) {
所以调用变成:
solve< n1_3, n2_3>(A_3, b_3);
关于c++ - 模板函数重载和 SFINAE 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49171904/
C++11 专家的几个问题。 我正在与 SFINAE 打交道,我遇到了一个奇怪的情况,其中 g++ (4.9.2) 和 clang++ (3.5.0) 的行为不同。 我准备了以下示例代码。很抱歉,我无
这些天我正在试验 SFINAE,有些事情让我很困惑。为什么 my_type_a不能在 my_function 中推导出来的实例化? class my_type_a {}; template clas
我正在尝试复制(我猜)典型的 SFINAE 示例来判断一个类型是否具有特定方法。我的代码基本上是 the one found in the accepted answer of a previous
出于学术原因,我想实现一个示例,如果非类型模板参数满足给定条件,则选择一个模板。例如,我想要一个只为奇数定义的函数。 可以这样做: template struct is_odd: public st
有没有办法检查两个可变参数包的串联是否与第三个可变参数包相同。 template struct ClassOne { } template struct ClassTwo { } template s
为什么以下代码无法编译?为具有特定成员的类型启用模板的最简洁的解决方案是什么?如果模板变量被直接用于初始化它的表达式替换,它也会编译。 #include template constexpr boo
代码: #include using std::nullptr_t; template using nullptr_vt = nullptr_t; struct not_addable{}; tem
我有一个模板类,我想有两个复制 ctor。一个用于平凡类型,另一个用于非平凡类型。 以下代码有效(使用一个拷贝 ctor): template struct MyStruct { MySt
我一直在尝试定义一个辅助类来帮助我使用模板方法,在该方法中我希望为复杂类型和实际类型提供通用实现。 到目前为止,这是我的尝试: #include #include template struct is
这个问题已经有答案了: Officially, what is typename for? [duplicate] (8 个回答) 已关闭 3 年前。 我正在研究现代 C++ 中的 SFINAE,我看
我有一个 std::variants 包含具有不同接口(interface)的对象。目标是如果变体中的对象具有某些方法,则调用它。 我正在尝试制作几个模板装饰器,并寻找一种方法来使用更少的样板和没有宏
我有一个代码,它接受一个函数并根据函数签名执行它,如下所示: template struct Value { int value[Num]; }; struct Executor { t
我一直在尝试定义一个辅助类来帮助我使用模板方法,在该方法中我希望为复杂类型和实际类型提供通用实现。 到目前为止,这是我的尝试: #include #include template struct is
在此视频中https://youtu.be/Vkck4EU2lOU?t=582 “标签调度”和SFINAE作为替代方案出现,允许实现所需模板功能的选择。 正确吗? “标签发送”不是使用SFINAE吗?
我认为下面的代码会编译,因为冲突的重载是 SFINAEd 了。但是编译器(GCC)说:void Foo::bar(Xd) const' cannot be overloaded .有没有简单的方法来修
#define BINDINGTEMPLATE template, int> || std::is_same_v, std::string> || std::is_same_v, char>>> 这样
SFINAE 是否在概念论证中起作用? (也许这里不叫 SFINAE)。例子: template requires std::invocable && // , void>)
在他的演讲现代模板元编程:纲要第 I 部分中,Walter Brown 以他的方式讨论了 enable_if 与 SFINAE 的交互。 在大约 47:40 的谈话中,他被问到一个问题,我无法完全匹配
我正在尝试对同一函数进行两次重载,称为某物。这个函数应该以另一个函数作为参数,并且它应该根据这个另一个函数的返回类型进行重载。到目前为止我有这个: #include #include using
以下代码特化了 f() 的两个版本。第一个检测一个 vector 并返回一个迭代器。第二个接受所有其他类型并返回一个拷贝。 这无法在 VC 2010 上编译,GetIter2 中有一个错误,即 Get
我是一名优秀的程序员,十分优秀!