最近我有建议span<T>在我的代码中使用's,或者在网站上看到了一些使用span's - 应该是某种容器的答案.但是 - 我在C++标准库中找不到类似的东西.
那么这个神秘的是什么span<T>,以及为什么(或什么时候)使用它是一个好主意,如果它是非标准的?
在最近的几次会议演示中,我听说Bjarne Stroustrup和其他人提到了C++的新编码指南以及支持它们的一些类型.
具体来说,我记得一个例子,span<T>而不是(T* p, int n)作为一个函数的参数(在约32:00进入谈话时); 但我也记得使用的建议array_view<T>.它们是两个替代方案但是相同的概念吗?或者我是否混淆了事情,他们实际上并没有那么相关?
我似乎无法找到任何关于它们应该是什么的权威定义.
c++ array-view cpp-core-guidelines guideline-support-library
最近提出了C++核心指南(恭喜!),我担心gsl::not_null类型.如I.12中所述:声明一个不能为null的指针not_null:
帮助避免解除引用nullptr错误.通过避免对nullptr进行冗余检查来提高性能.
...
通过声明源代码中的意图,实现者和工具可以提供更好的诊断,例如通过静态分析查找某些类错误,并执行优化,例如删除分支和空测试.
目的很明确.但是,我们已经有了语言功能.不能为null的指针称为引用.虽然引用一旦创建就无法反弹,但这个问题可以解决std::reference_wrapper.
我gsl::not_null和之间的主要区别在于,std::reference_wrapper后者只能用于代替指针,而前者适用于任何事物 - 可nullptr分配(引自 F.17:使用not_null来表示"null"不是有效值):
not_null不只是内置指针.它的工作原理为array_view,string_view,unique_ptr,shared_ptr,和其他类似指针的类型.
我想象功能比较表如下:
T&:
nullptr?- 是的std::reference_wrapper<T>:
nullptr?- 是的gsl::not_null<T*>:
nullptr?- 是的现在这里是问题,最后:
std::reference_wrapper现在没用了?PS我创建了标签cpp-core-guidelines,guideline-support-library为此,我希望正确.
在C++ Core Guidlines P.1 change_speed示例中,它显示了一个Speed使用的类型,如下所示:
change_speed(Speed s); // better: the meaning of s is specified
// ...
change_speed(2.3); // error: no unit
change_speed(23m / 10s); // meters per second
Run Code Online (Sandbox Code Playgroud)
我对这个例子的最后两行特别感兴趣.第一个似乎暗示如果你没有提供带有参数的单位change_speed就会抛出一个错误.最后一行显示使用一些m和s文字定义的单位.现代版C++中是否都有这些新功能?如果是这样,将如何实现这样的东西,以及需要什么版本的C++?
在阅读Bjarne Stroustrup的CoreCppGuidelines时,我找到了一条与我的经历相矛盾的指南.
该C.21要求如下:
如果您定义或
=delete任何默认操作,请定义或=delete全部
原因如下:
特殊函数的语义密切相关,因此如果需要非默认函数,则其他人也需要修改.
根据我的经验,重新定义默认操作的两种最常见情况如下:
#1:使用默认主体定义虚拟析构函数以允许继承:
class C1
{
...
virtual ~C1() = default;
}
Run Code Online (Sandbox Code Playgroud)
#2:默认构造函数的定义,对RAII类型的成员进行一些初始化:
class C2
{
public:
int a; float b; std::string c; std::unique_ptr<int> x;
C2() : a(0), b(1), c("2"), x(std::make_unique<int>(5))
{}
}
Run Code Online (Sandbox Code Playgroud)
根据我的经验,所有其他情况都很少见.
您如何看待这些例子?它们是C.21规则的例外,还是最好在这里定义所有默认操作?还有其他常见的例外吗?
Cpp核心指南中的例子浪费了什么?
P.9:不要浪费时间或空间
[...]
Run Code Online (Sandbox Code Playgroud)void lower(zstring s) { for (int i = 0; i < strlen(s); ++i) s[i] = tolower(s[i]); }是的,这是生产代码中的一个例子.我们留给读者来弄清楚浪费了什么.
来自https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rp-waste
我的类中有一个私有静态向量,它保存一个指向所有从它创建的对象的指针。这是必要的,因为每个对象都需要访问来自所有其他对象的信息来执行一些计算:
// Header file:
class Example {
public:
Example();
private:
static std::vector<const Example*> examples_;
};
// Cpp file:
std::vector<const Example *> Example::examples_ = {};
Example::Example() {
// intialization
examples_.emplace_back(this);
}
void Example::DoCalc() {
for (auto example : examples_) {
// do stuff
}
}
Run Code Online (Sandbox Code Playgroud)
clang-tidy指出我违反了 C++ 核心指南,即:“变量 'examples_' 是非常量且全局可访问的,考虑将其设置为常量(cppcoreguidelines-avoid-non-const-global-variables)”。
就我个人而言,我没有看到我的代码与核心指南中的示例代码之间的相似之处,尤其是因为该变量在一个类中并且是私有的。实现此功能的“正确”方式是什么?如果可以避免,我不想从 clang-tidy 禁用此检查。
根据C++核心指南,我应该使用gsl :: span来传递半开序列.
我认为这意味着不要像以下那样编写函数:
void func(const std::vector<int>& data) {
for (auto v : data) std::cout << v << " ";
}
Run Code Online (Sandbox Code Playgroud)
我更喜欢:
void func(gsl::span<const int> data) {
for (auto v : data) std::cout << v << " ";
}
Run Code Online (Sandbox Code Playgroud)
其优点在于它不会假定调用者将数据放入a中vector,或强制他们构造临时数据vector.他们可以通过一个std::array例子.
但一个常见的用例是传递一个括号括起来的初始化列表:
func({0,1,2,3})
Run Code Online (Sandbox Code Playgroud)
这适用于一个功能,std::vector但是为了一个功能,gsl::span我得到了错误信息:
错误C2664:'void func(gsl :: span)':无法将参数1从'initializer-list'转换为'gsl :: span'
它看起来像gsl::span 一个模板化的构造函数,旨在采取任何容器.
这只是Microsoft GSL实现中缺少的东西还是有充分理由阻止这种做法?
我正在使用 C++ 开始一个项目,我之前在少数学校项目之外从未使用过它 - 远不及我现在正在处理的范围。
我的目标是在我努力避免错误、提高性能以及最重要的是:提高代码的可维护性时,尽我最大的努力遵循C++ 核心指南。
我已经遇到了数百个问题,从我的 g++/Clang++ 版本不正确到标准库没有被发现到 g++ 使用错误版本的 C++ 编译到非常基本的函数没有按预期运行 -而我没有甚至开始研究 autotools,所以我预计会有更多的麻烦。
不过,这个问题特定于 C++ 核心指南的一部分。接口 6:优先使用 Expects() 来表达前提条件
我尝试编写以下简单代码:
#include <iostream>
using namespace std;
int square(int x) {
Expects(x > 0);
return x * x;
}
int main() {
cout << square(3) << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这在 g++ 中引发了一个错误:
$> g++ -std=c++17 main.cpp
main.cpp: In function ‘int square(int)’:
main.cpp:7:2: error: ‘Expects’ was not declared in this scope
Expects(x …Run Code Online (Sandbox Code Playgroud) "// not null"在第 N 次将注释添加到原始指针后,我再次想知道not_null模板发生了什么。
C++ 核心指南是在很久以前创建的,并且一些内容已经纳入标准,例如std::span(有些内容类似于string_view核心std::array指南本身并且起源于核心指南本身,但有时会被混淆)。鉴于其相对简单,为什么 not_null (或类似的东西)还没有将其纳入标准?
我定期扫描 ISO 邮件(但可能不彻底),而且我什至不知道有这样的建议。
可能回答我自己的问题。我不记得遇到过任何可以防止我编写的代码出现错误的情况,因为我们尽量不以这种方式编写代码。
这些指南本身很受欢迎,例如 clang-tidy 和声纳。支持库似乎不太受欢迎。
例如,boost 从一开始就作为 Linux 上的软件包提供。我不知道 GSL 有任何实现。不过,我认为它与 Windows 上的 Visual C++ 捆绑在一起。
既然评论里有人问了。
我自己会用它来记录意图。像这样的构造not_null<>可能具有注释所没有的语义价值。尽管我可以看到它的位置,但执行它是次要的。这最好以零开销来完成(也许仅在有限数量的情况下在编译时)。
我主要考虑的是原始指针成员变量的情况。我忘记了将指针传递给函数的情况,对于该函数,我总是使用引用来表示非空,也表示“我不取得所有权”。
同样(对于班级成员)我们也可以记录所有权owned<> not_owned<>。
我想还有是否允许更改关联对象。但这可能太高了。您可以使用引用成员而不是指针来记录这一点。我自己避免引用成员,因为我几乎总是想要可复制和可分配的类型。但是,请参见示例:我应该在成员数据中选择指针还是引用?对此进行一些讨论。
另一个维度是另一个实体是否可以修改变量。“const”表示我保证不修改它。在多线程代码中,我们想说的几乎相反。那就是“其他代码承诺在我们使用它时不会修改它”(没有显式锁定),但这远离主题......
c++ ×10
c++11 ×2
array-view ×1
c++-faq ×1
c++17 ×1
c++20 ×1
clang-tidy ×1
oop ×1
pointers ×1
std-span ×1