- 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语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!