C++20 引入了概念,这是一种对模板函数或类可以采用的类型施加约束的智能方法。
虽然迭代器类别和属性保持不变,但改变的是执行它们的方式:C++17 之前使用标记,C++20 以来使用概念。例如,您可以使用 std::forward_iterator 概念来标记迭代器,而不是使用 std::forward_iterator_tag 标签。
同样的事情也适用于所有迭代器属性。例如,前向迭代器必须是 std::incrementable。这种新机制有助于获得更好的迭代器定义,并使编译器中的错误更具可读性。
这段文字摘自这篇文章: https ://www.internalpointers.com/post/writing-custom-iterators-modern-cpp
但作者并没有升级如何用概念在C++20上制作自定义迭代器的内容,仍然是<= C++17标签版本。
有人可以举例说明如何使用概念功能在 C++20 版本中为自定义容器编写自定义迭代器吗?
说我有以下(非常简单)的代码。
#include <iostream>
int main() {
std::cout << std::stoi("12");
}
Run Code Online (Sandbox Code Playgroud)
这在g ++和clang上都可以编译。但是,它无法在MSVC上编译,并出现以下错误:
错误C2039:“ stoi”:不是“ std”的成员
错误C3861:“ stoi”:找不到标识符
我知道这std::stoi是<string>标头的一部分,大概前两个编译器作为标头的一部分<iostream>而后者不包含。根据C ++标准[res.on.headers]
C ++标头可以包括其他C ++标头。
对我来说,这基本上说所有三个编译器都是正确的。
当我的一个学生提交作业时,这个问题就出现了,TA标记为未编译。我当然去修理了。但是,我想防止将来发生此类事件。因此,有没有一种方法可以确定应该包括哪些头文件,而无需在每次检查的三个不同编译器上进行编译?
我能想到的唯一方法是确保对于每个std函数调用都存在一个适当的include。但是,如果您现有的代码长数千行,则搜索起来可能很乏味。有没有更简单/更好的方法来确保交叉编译器的兼容性?
三种编译器的示例:https : //godbolt.org/z/kJhS6U
假设我有一个看起来像这样的类(这只是一个例子):
class A {
double *ptr;
public:
A() : ptr( new double[100] ) {}
A( const A &other ) {
other.ptr[7] = 15;
}
void doNotChangeMyData() const {
ptr[43] = 14;
}
void changeMyData() {
ptr[43] = 14;
}
~A() { delete[] ptr; }
};
Run Code Online (Sandbox Code Playgroud)
该const在拷贝构造函数和两个doNotChangeMyData功能让这个ptr不能改变的; 但是,这仍然允许我修改指向的数组的内容ptr。
有没有办法防止仅ptr在const实例中更改 's 数组的内容,而不是“小心”(或远离原始指针)?
我知道我可以做类似的事情
void doNotChangeMyData() const {
const double *const ptr = this->ptr;
ptr[43] = 14; // then this would …Run Code Online (Sandbox Code Playgroud) 这是我的代码。编译时出现错误
\n\n\n\n\n\xe2\x80\x98geometry\xe2\x80\x99 之前的声明符无效
\n
在第 16 行和第 48 行,我不确定我做错了什么。请指教。
\n\n#include <iostream>\n#include <memory>\n#include <vector>\nusing namespace std;\nclass FactGeometry { //Factory class\npublic:\n static std::shared_ptr<FactGeometry>geometry( int choice );\n virtual void calcArea() = 0;\n};\n\nclass CalcRectangle :public FactGeometry {\n void calcArea() {\n double ll, bb, Area;\n std::cout << "\\nEnter the length = ";\n std::cin >> ll;\n std::cout << "\\nEnter the breadth = ";\n std::cin >> bb;\n Area = ll * bb;\n std::cout << "\\nArea = " << Area;\n }\n}; //end class\n\nclass CalcTraingle …Run Code Online (Sandbox Code Playgroud) 我接管了一些代码,并遇到了一个奇怪的数组重新分配。这是一个 Array 类中的函数(由 JsonValue 使用)
void reserve( uint32_t newCapacity ) {
if ( newCapacity > length + additionalCapacity ) {
newCapacity = std::min( newCapacity, length + std::numeric_limits<decltype( additionalCapacity )>::max() );
JsonValue *newPtr = new JsonValue[newCapacity];
if ( length > 0 ) {
memcpy( newPtr, values, length * sizeof( JsonValue ) );
memset( values, 0, length * sizeof( JsonValue ) );
}
delete[] values;
values = newPtr;
additionalCapacity = uint16_t( newCapacity - length );
}
}
Run Code Online (Sandbox Code Playgroud)
我明白这一点;它只是分配一个新数组,并将旧数组中的内存内容复制到新数组中,然后将旧数组的内容清零。我也知道这样做是为了防止调用析构函数和移动。
这JsonValue是一个带有函数的类,以及一些存储在联合中的数据(字符串、数组、数字等)。
我担心的是这是否实际上是定义的行为。我知道它有效,并且自从我们几个月前开始使用它以来一直没有问题;但如果它未定义,那么并不意味着它会继续工作。 …
以下是一些代码的简化版本。
struct Test {
Test( int &id ) : id( id ) {}
int &id;
};
struct B : Test {
B() : Test( a ) {}
int a;
};
Run Code Online (Sandbox Code Playgroud)
现在,我知道在这种情况下,当创建对象时,父级Test将在B对象之前创建。B那么这是否意味着a传递给Test构造函数的变量还没有地址,因此是未定义的行为?或者这样安全吗?
只是为了澄清,直到完全构建之后id才使用的值。B
假设我有一个这样的整数向量,std::vector<int> _data;
我知道如果我想从中删除多个项目_data,那么我可以简单地调用
_data.erase( std::remove_if( _data.begin(), _data.end(), [condition] ), _data.end() );
Run Code Online (Sandbox Code Playgroud)
这比eraseing 多个元素要快得多,因为vector. 我想知道插入是否有类似的东西。
例如,如果我有以下对
auto pair1 = { _data.begin() + 5, 5 };
auto pair2 = { _data.begin() + 12, 12 };
Run Code Online (Sandbox Code Playgroud)
我可以使用一些现有std函数在一次迭代中插入这两个吗?我知道我可以这样做:
_data.insert( pair2.first, pair2.second );
_data.insert( pair1.first, pair1.second );
Run Code Online (Sandbox Code Playgroud)
但这对于大向量(谈论 100,000+ 个元素)来说(非常)慢。
编辑:基本上,我有一个自定义集(和地图),它使用 avector作为底层容器。我知道我可以只使用std::setor std::map,但是我执行的遍历次数远远超过插入/删除次数。从 aset和切换map到这个自定义集/地图已经减少了 20% 的运行时间。但目前,插入占用了大约 10% 的剩余运行时间,因此减少这一点很重要。
不幸的是,该订单也是必需的。我尽可能使用unordered_版本,但在某些地方,顺序确实很重要。
我有一个关于类结构、填充和由此产生sizeof的类的快速问题。在下面的示例中,在我测试过的每个编译器上,结果始终为 40 个字节sizeof A,这对我来说很有意义。
#include <iostream>
class A {
int a, b; // 4 + 4
short c; // + 2
double f; // not currently 8 byte aligned. Currently 10 bytes, so pad 6 extra bytes, so: + 6 + 8
char w; // + 1
bool d; // + 1
double g; // not currently 8 byte aligned. Currently 26 bytes, so pad 6 extra bytes, so: + 6 + 8
// 4 + …Run Code Online (Sandbox Code Playgroud) 我似乎从 Visual Studio 2019(16.5 预览版,但也在 16.4 及更早版本中)代码分析工具收到错误警告消息。这是一个错误,还是我真的只是错过了什么?
生成的警告(确切地说)是:
警告 C6385:从“prodlist”读取无效数据:可读大小为“(size_t)*32+8”字节,但可以读取“64”字节。
这是生成警告的代码(尽可能少)
#include <cstdint>
#include <string>
#include <iostream>
struct Product {
std::string price_profile;
};
int getNumRows() {
return 5;
}
Product *getProductsFromDB( int &numelements ) {
numelements = 0;
const int num_rows = getNumRows();
if ( num_rows == 0 ) {
numelements = 0;
return nullptr;
}
Product *prodlist = new Product[num_rows];
for ( int i = 0; i < num_rows; ++i ) {
prodlist[i].price_profile = "test"; // Warning on this …Run Code Online (Sandbox Code Playgroud) 我创建了我认为最简单的 GTK 4 应用程序来创建一个带有菜单栏的窗口,GAction当单击菜单项时,该菜单栏会激活 。
#include <stdio.h>
#include <assert.h>
#include <gtk/gtk.h>
static void
activate_quit(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
printf("Quit activated\n");
}
static void
startup(GApplication *app, gpointer user_data)
{
assert(user_data == NULL);
char *menubar_ui = (
"<interface>"
" <menu id='menubar'>"
" <submenu>"
" <attribute name='label' translatable='yes'>_File</attribute>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>_Quit</attribute>"
" <attribute name='action'>app.quit</attribute>"
" <attribute name='accel'><Primary>q</attribute>"
" </item>"
" </section>"
" </submenu>"
" </menu>"
"</interface>"
);
GtkBuilder *builder = gtk_builder_new_from_string(menubar_ui, -1);
GObject *menubar …Run Code Online (Sandbox Code Playgroud) c++ ×8
c ×1
c++-concepts ×1
c++11 ×1
c++20 ×1
constants ×1
glib ×1
gtk ×1
gtk4 ×1
make-shared ×1
sal ×1
shared-ptr ×1
sizeof ×1
vector ×1