据我所知,引入了C++ 14 std::make_unique
,因为由于没有指定参数评估顺序,这是不安全的:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
Run Code Online (Sandbox Code Playgroud)
(说明:如果评估首先为原始指针分配内存,则g()
在std::unique_ptr
构造之前抛出调用和异常,然后内存泄漏.)
呼叫std::make_unique
是一种约束呼叫顺序的方法,从而使事情变得安全:
f(std::make_unique<MyClass>(param), g()); // Syntax B
Run Code Online (Sandbox Code Playgroud)
从那以后,C++ 17澄清了评估顺序,使得Syntax A也安全,所以这里是我的问题:在C++ 17中仍然有理由使用std::make_unique
over std::unique_ptr
的构造函数吗?你能举一些例子吗?
截至目前,我能想象的唯一原因是它只允许输入MyClass
一次(假设您不需要依赖多态std::unique_ptr<Base>(new Derived(param))
).但是,这似乎是一个非常弱的原因,特别是当构造函数std::make_unique
不允许指定删除器时std::unique_ptr
.
而且为了清楚起见,我并不是主张std::make_unique
从标准库中删除(至少为了向后兼容而保持它有意义),而是想知道是否仍然存在强烈倾向于它的情况std::unique_ptr
所以,我写了这样的东西
#include <iostream>
using namespace std;
void f(int32_t i)
{
cout << "int32: " << i << endl;
}
void f(int16_t i)
{
cout << "int16: " << i << endl;
}
void f(int8_t i)
{
cout << "int8: " << i << endl;
}
void f(uint32_t i)
{
cout << "uint32: " << i << endl;
}
void f(uint16_t i)
{
cout << "uint16: " << i << endl;
}
int main() {
uint8_t i = 0u;
f(i);
return …
Run Code Online (Sandbox Code Playgroud) 我想知道是否有必要声明constexpr函数和方法的限制,就像内联函数和方法一样.
我知道必须在头文件中编写内联函数或方法,以使编译器能够访问它们被调用的定义.如果constexpr有类似的东西会有意义,但我无法找到关于这一点的任何东西......
基本上我的问题是:
我是否可以在头文件中编写constexpr函数的定义而不会冒重复符号的风险?
我可以分离constexpr函数或方法的声明和定义吗?
我有一些看起来像这样的代码:
namespace myLibrary
{
class A
{
public:
struct Nested
{
...
};
...
};
}
Run Code Online (Sandbox Code Playgroud)
在代码的其他部分,我需要访问A.因为我喜欢可读的代码,我也喜欢using指令:
using myLibrary::A;
...
A a;
Run Code Online (Sandbox Code Playgroud)
现在,在某些时候我还需要访问我的嵌套类,所以我想写这样的东西:
using myLibrary::A::Nested;
Run Code Online (Sandbox Code Playgroud)
显然,编译器不能知道这是一个嵌套类而不是类成员,并给我一个错误:
error : using declaration can not refer to class member
Run Code Online (Sandbox Code Playgroud)
我无法理解的是为什么这不能解决问题:
using typename myLibrary::A::Nested;
Run Code Online (Sandbox Code Playgroud)
编译器仍然给我完全相同的错误!
幸运的是,我有其他选择:
// Using a typedef
typedef myLibrary::A::Nested Nested;
// Using the new C++11 syntax for type aliasing
using Nested = myLibrary::A::Nested;
Run Code Online (Sandbox Code Playgroud)
但我想了解为什么使用typename指令不起作用.它不符合我的想法吗?或者它不是由编译器实现的?如果是后者,是否有理由呢?
在c ++ 11之前,我曾经写过这样的代码:
// Small functions
void doThingsWithA(const A& a)
{
// do stuff
}
void doThingsWithB(const B& b)
{
// do stuff
}
void doThingsWithC(const C& c)
{
// do stuff
}
// Big function
void doThingsWithABC(const A& a, const B& b, const C& c)
{
// do stuff
doThingsWithA(a);
doThingsWithB(b);
doThingsWithC(c);
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
但是现在,使用移动语义,允许我的函数将rvalue引用作为参数并添加这些重载可能会变得有趣(至少在某些情况下):
void doThingsWithA(A&& a);
void doThingsWithB(B&& b);
void doThingsWithC(C&& c);
Run Code Online (Sandbox Code Playgroud)
从我收集的内容来看,如果我想在我的大函数中调用那些重载,我需要使用完美的转发,这可能看起来像这样(它的可读性稍差,但我想它可以用模板类型的良好命名约定):
template<typename TplA, typename TplB, typename TplC>
void doThingsWithABC(TplA&& a, TplB&& b, TplC&& …
Run Code Online (Sandbox Code Playgroud) c++ perfect-forwarding c++11 universal-reference forwarding-reference
几天前我发现了统一初始化,我看到每个人都应该尽可能地使用它.
但是,我不禁想到这种新语法比它的价值更麻烦......
第一个例子
假设我写了一个库,其中我有一个像这样的结构:
struct MyStruct
{
int member0;
int member1;
}
Run Code Online (Sandbox Code Playgroud)
用户可以使用聚合初始化来编写类似的东西:
MyStruct myVar = {0, 1}; // member0 = 0 and member1 = 1
Run Code Online (Sandbox Code Playgroud)
现在,让我们说我更新了我的库,结构现在看起来像这样:
struct MyStruct
{
int member0;
int member1;
MyStruct(int p0, int p1) : member0(p1), member1(p0){}
}
Run Code Online (Sandbox Code Playgroud)
在C++ 11之前,用户代码将停止编译,这会强制用户重写他的代码并使用构造函数.但是现在,代码将被编译并被解释为统一初始化:
MyStruct myVar = {0, 1}; // member0 = 1 and member1 = 0
Run Code Online (Sandbox Code Playgroud)
没有用户知道,更新他的库将使他的代码做一些非常不同的事情!
第二个例子
现在,让我们说我的库里有这样一个类:
class MyClass
{
public:
MyClass(int size, int default = 0) : elements(size, default){}
private:
std::vector<int> elements;
}
Run Code Online (Sandbox Code Playgroud)
用户可以像这样使用它:
MyClass myVar …
Run Code Online (Sandbox Code Playgroud) 我有这个代码:
#include <cstdint>
#include <deque>
#include <iostream>
int main()
{
std::deque<uint8_t> receivedBytes;
int nbExpectedBytes = 1;
if (receivedBytes.size() >= static_cast<size_t>(nbExpectedBytes))
{
std::cout << "here" << std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用 -Wsign-conversion,这在我的 linux 笔记本电脑上编译时不会发出警告,但在要运行的嵌入式 linux 上,我收到以下警告:
temp.cpp:在函数“int main()”中:temp.cpp:10:33: 警告:从“int”转换为“std::deque::size_type {aka long unsigned int}”可能会改变结果 [-Wsign-conversion]
Run Code Online (Sandbox Code Playgroud)if (receivedBytes.size() >= static_cast<size_t>(nbExpectedBytes)) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我就是不明白:
int
to 转换size_t
(它不应该产生警告,因为转换是显式的),然后将 asize_t
与 a进行比较std::deque<unsigned char>::size_type
,那么从有符号到无符号的隐式转换在哪里触发警告??!我不禁认为嵌入式linux上的编译器在这里是错误的。我错过了什么吗?
编辑:在我的 linux 笔记本电脑上,我使用的是 g++ 9.3.0 版,而在嵌入式 linux 上,我使用的是 …
假设我们需要遍历一个容器。传统的for循环如下所示:
for (auto it = container.begin(), end = container.end();
it != end;
++it)
{
doStuff(*it);
}
Run Code Online (Sandbox Code Playgroud)
而基于范围的for如下所示:
for (auto& element : container)
{
doStuff(element);
}
Run Code Online (Sandbox Code Playgroud)
现在,在开发的某个时刻,我们意识到由于某种原因,我们需要在这些循环迭代中增加其他内容。
需要增加的可能是各种各样的东西。例如,如果我们将相关数据存储在相同大小的其他容器中,那么在进行迭代时,我们可能也需要将迭代器增加到这些容器中(尽管我希望标准库的未来版本能够使我们做更多的事情)通过结构化绑定和标准版本的boost :: range :: combine或其他方式来表达。
在下面,为简单起见,我们假设要为每个元素分配一个ID,因此需要增加的只是一个计数器。
现在,传统循环看起来像这样:
unsigned int elementID = 0u;
for (auto it = container.begin(), end = container.end();
it != end;
++it, ++elementID)
{
doStuff(*it, elementID);
}
Run Code Online (Sandbox Code Playgroud)
几乎任何东西都必须更改,并在++elementID
后面加上,以++it
确保无论每次迭代后计数器如何递增计数器。即使另一个程序员修改了循环的主体,并说在某些条件下提前进入下一个迭代continue
,也不会冒着他们忘记增加计数器之类的风险。
现在,据我所知,使用基于范围的for进行增量的唯一方法是执行以下操作:
unsigned int elementID = 0u;
for (auto& element : container)
{
doStuff(element, elementID);
++elementID;
}
Run Code Online (Sandbox Code Playgroud)
也就是说,将增量放入循环体内。
这方面的表现力较差 …
我有这样的代码:
\n#include <iostream>\nusing std::cout;\nusing std::endl;\n\nint main() {\n uint16_t a = 0;\n uint16_t b = 0;\n\n if ( a - b < 3u )\n {\n cout << "cacahu\xc3\xa8te" << endl;\n }\n return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n当我使用 g++ with 编译它时-Wall
,我得到:
temp.cpp: In function \xe2\x80\x98int main()\xe2\x80\x99:\ntemp.cpp:9:13: warning: comparison of integer expressions of different signedness: \xe2\x80\x98int\xe2\x80\x99 and \xe2\x80\x98unsigned int\xe2\x80\x99 [-Wsign-compare]\n 9 | if ( a - b < 3u )\n | ~~~~~~^~~~\n
Run Code Online (Sandbox Code Playgroud)\n如果我改为写入,则不会显示该警告if ( a - b < static_cast<uint16_t>(3u) …
标题是满口的,但基本上我写了这样的东西:
enum EnumType{ValA, ValB};
template<EnumType> class A {};
template<>
class A<ValA>
{
private:
double param;
public:
A(double param);
};
template<>
A<ValA>::A(double param)
{
// Do Stuff
}
Run Code Online (Sandbox Code Playgroud)
当我尝试编译它时,我得到:
错误:'A <(EnumType)0u> :: A(double)'的template-id'A <>'与任何模板声明都不匹配
我做错了吗?
在网上搜索类似的情况后,我试图删除template<>
(即使我不明白为什么这会起作用),但后来我得到了
'A <(EnumType)0u> :: A(double)'的多重定义
我想,我可以代替template<>
通过inline
(我试着和它编译),但是,这并不觉得自己是正确的方式做到这一点(或者,如果是这样,我不明白为什么).
有人可以向我解释我写的内容有什么问题,为什么改变它似乎有效,以及正确的方法是什么?
我对使用声明有点困惑.我理解using foo::bar;
将符号bar
从命名空间foo
导入当前命名空间,但这是静态还是动态发生的?
更具体地说,使用声明会导致开销吗?是否可以根据条件导入具有相同名称的不同符号?(这将是不好的做法,但我很好奇所有相同)
感觉它应该是静态的,但我找不到任何东西来证实这一点......
c++ ×10
c++11 ×6
c++17 ×1
constexpr ×1
for-loop ×1
integer ×1
nested-class ×1
overloading ×1
templates ×1
typename ×1
unique-ptr ×1
using ×1