format_disk
如果以下程序从未在代码中调用过程,如何调用它?
#include <cstdio>
static void format_disk()
{
std::puts("formatting hard disk drive!");
}
static void (*foo)() = nullptr;
void never_called()
{
foo = format_disk;
}
int main()
{
foo();
}
Run Code Online (Sandbox Code Playgroud)
这与编译器不同.通过优化启用Clang进行编译,该函数never_called
在运行时执行.
$ clang++ -std=c++17 -O3 a.cpp && ./a.out
formatting hard disk drive!
Run Code Online (Sandbox Code Playgroud)
但是,使用GCC进行编译时,此代码只会崩溃:
$ g++ -std=c++17 -O3 a.cpp && ./a.out
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)
编译器版本:
$ clang --version
clang version 5.0.0 (tags/RELEASE_500/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ gcc --version
gcc (GCC) 7.2.1 20171128
Copyright (C) …
Run Code Online (Sandbox Code Playgroud) Run Code Online (Sandbox Code Playgroud)constexpr reference operator[](index_type idx) const; constexpr reference operator()(index_type idx) const;
返回
idx
对序列的-th元素的引用.如果idx
超出范围(即,如果它小于零或大于或等于size()
),则行为是未定义的.
是有意义的超载operator[]
进行索引,作为量程表示的对象可以是指对象的连续序列,但为什么是operator()
,该函数调用操作符,也重载用于相同目的?我不相信标准库中有类似的东西.
使用转发引用时,将相同的值转发给多个函数是不是一个坏主意?考虑以下代码:
template<typename Container>
constexpr auto
front(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).front(); }
template<typename Container>
constexpr auto
back(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).back(); }
template<typename Container>
constexpr auto
get_corner(Container&& c)
{
return do_something(front(std::forward<Container(c)),
back(std::forward<Container>(c));
}
Run Code Online (Sandbox Code Playgroud)
如果Container
是左值引用,则该函数可以正常工作.但是,我担心rvalues传递给它的情况,因为一旦移动操作发生,该值将被无效.我的疑问是:在这种情况下,是否有正确的方法来转发容器,而不会丢失价值类别?
列表初始化({...}
语法)不允许缩小转换.例如,尝试列表初始化一个int i
with会3.14f
产生编译错误,因为从浮点值到整数的转换正在缩小:
<source>:11:32: error: narrowing conversion of '3.1400001e+0f' from 'float' to 'int' inside { } [-Wnarrowing]
int i{3.14f};
^
Run Code Online (Sandbox Code Playgroud)
随着中说,为什么它可以构造一个float f
与3.14
,这是类型的double
?(从转换double
到float
被认为是缩小的.)执行以下操作:
float f{3.14};
Run Code Online (Sandbox Code Playgroud)
不保留编译错误.
在C++ 17中,可以在不指定模板类型的情况下实例化对象.基本上,这段代码会编译:
std::pair p(2, 4.5); // deduces to std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // same as auto t = std::make_tuple(4, 3, 2.5);
Run Code Online (Sandbox Code Playgroud)
所以,假设以下代码:
template<typename... Ts>
struct Foo
{
Foo(Ts&&... ts) :
ts{std::forward_as_tuple(ts...)}
{}
std::tuple<Ts...> ts;
};
int main()
{
auto f = [] { return 42; };
Foo foo{f, [] { return 84; }};
}
Run Code Online (Sandbox Code Playgroud)
我应该std::decay
在这样的元组声明中使用吗?
std::tuple<std::decay_t<Ts>...> ts;
Run Code Online (Sandbox Code Playgroud)
因为这是我根据推导出的模板类型编写函数来返回对象的方法:
template<typename T>
auto make_baz(T&& t) -> baz<std::decay_t<T>>;
Run Code Online (Sandbox Code Playgroud)
我可以在Foo的构造函数中看到这种模式,它使用转发引用将值正确地传递给元组.我不确定这里的类型演绎是否表现相同.
让我们从一个最小的例子开始:
#include <utility>
int main()
{
auto [a, b] = std::pair(1, 'A');
return a;
}
Run Code Online (Sandbox Code Playgroud)
与GCC 7.3编译通过-std=c++17
,并-Wunused-variable
,并运行它:
<source>: In function 'int main()':
<source>:5:15: warning: unused variable 'b' [-Wunused-variable]
auto [a, b] = std::pair(1, 'A');
^
Run Code Online (Sandbox Code Playgroud)
海湾合作委员会可能正确报告缺席使用b
,但错误地将其称为变量.引用[dcl.struct.bind]/1:
结构化绑定声明将标识符列表的标识符 v0,v1,v2,... 引入为结构化绑定的名称([basic.scope.declarative]).
所以b
显然不是变量,而是名称.使用Clang 6.0.0编译相同的代码,并使用相同的标志,我们不会得到任何警告.return a;
从代码中删除语句:
#include <utility>
int main()
{
auto [a, b] = std::pair(1, 'A');
// return a;
}
Run Code Online (Sandbox Code Playgroud)
并使用Clang再次编译它,我们得到:
<source>:5:10: warning: unused variable …
Run Code Online (Sandbox Code Playgroud) 从N4720 C++模块草案中,[basic.def.odr]/6说:
[......] 对于拥有出口申报单的实体,该实体只应有一个定义; 仅当模块的抽象语义图包含实体的定义时才需要诊断.[注意:如果定义不在接口单元中,则最多一个模块单元可以拥有并使用该定义. - 尾注] [...]
根据我的理解,实际上模板只有一次机会被编译器解析一次,与当前事态形成对比(每个翻译单元的定义都有一个精确的副本).这对于具有类似情况的其他实体也是有效的,例如内联函数/变量.
我的问题来自这样一个事实:由于每个翻译单元最多只能有一个实体定义(如[basic.def.odr]/1中所述),因此在TU中对实体进行不同的定义是未定义的行为. .并且,由于导出的实体在整个编译单元中只有一个定义(使未实现的定义对于它们的实现单元是唯一的),从我的角度来看,定义错误即使不是不可能也是更难的.
最后,简单地说:将(或确实或应该)模块的使用使得不可能违反ODR规则,或者更难以出错?
我开发了一个必须与不同字符集编码兼容的应用程序。为此,我总是使用TCHAR*
而不是char*
定义字符串。因此我用来_tcslen
获取字符串的大小。
今天,我在公司的版本控制系统上看到我的一位同事编辑了我编写的行以代替_tcslen
使用_tcsclen
。
我发现的唯一一个谈论这个函数特殊性的链接是这个,它没有解释这些函数之间的区别。
_tcslen
有人可以解释一下和之间的区别_tcsclen
吗?
简而言之,以下代码是否被认为具有未定义的行为?
int main()
{
int *p = <some invalid pointer value>;
}
Run Code Online (Sandbox Code Playgroud)
有关编译示例,请使用以下代码:
int main()
{
int *p = new int;
delete p; // Now p has an invalid pointer value.
int *q = p; // UB?
}
Run Code Online (Sandbox Code Playgroud)
我对这个主题做了一些研究,所以这些是我到目前为止发现的相关信息:
指针值(根据cppreference)可以是以下之一:
另外,根据cppreference,
通过无效指针值间接并将无效指针值传递给释放函数具有未定义的行为.无效指针值的任何其他使用都具有实现定义的行为.
该线程解决了无效指针的一些用途.具体而言,这个答案提到了基本原理文件(C99),其中有以下段落(第6.3.2.3节):
无论如何创建无效指针,任何使用它都会产生未定义的行为.甚至赋值,与空指针常量的比较或与自身的比较,在某些系统上可能会导致异常.
我不确定C++的状态是什么,但我认为,鉴于链接线程的答案,使用无效指针会导致未定义的行为.但请注意,该分配与初始化不同,因此我不确定初始化是否被视为一种用法.
由于Haskell函数只有一个参数,其余参数保持为lambdas,那么我们可以这样做:
foo a b = a + b -- this is like foo a = \b -> a + b
foo 1 2 -- ok
Run Code Online (Sandbox Code Playgroud)
好吧,我注意到如果我声明函数返回一个lambda,就像在注释中一样,这个方法foo 1 2
也可以正常工作.
但是当我编写这些函数时,像这样:
foo a b = a + b
bar x = x * x
bar . foo 1 2 -- oh, it's wrong, I need do '(bar . foo 1) 2'
Run Code Online (Sandbox Code Playgroud)
...这会返回错误.
好的,问题是:为什么不从功能组合中返回lambda函数组合?我的意思是,在构图中我需要在括号周围加上括号,当从函数返回lambda时不需要括号.