小编Dam*_*mon的帖子

Lambda传递给未定义的模板

今天我正在玩C++ 17的类模板参数演绎.想到的第一个明显的想法是传递一个可调用的模板参数.一个可调用的,其中一个是lambda,为什么不呢.我们试试吧.

template<typename F> class foo
{
F f;
public:
    foo(F in) : f(in) { f(); /* not very useful, admitted */ }
};

void bar() { puts("a"); }

int main()
{
    auto a = foo(bar);
    auto b = foo([](){ puts("b"); });

    return (void) a, (void) b, 0;
}
Run Code Online (Sandbox Code Playgroud)

以下是clang(5.0,r300688)对此的评价:

警告:函数'<(lambda at [source location])>'具有内部链接但未定义

代码编译并且肯定"工作正常",但警告表明编译器并不完全满意.

我已经准备好同意lambda有内部链接(匿名它在同一个翻译单元的其他地方无法访问,所以肯定它在另一个翻译单元中无法访问),但是怎么样呢.我不想从另一个翻译单元访问它.
关于缺乏定义的部分让我感到好笑,我甚至不知道如何在没有定义的情况下编写lambda.

总结:什么给出?该怎么做?我不喜欢警告,不仅它们使构建不那么漂亮,而且它们通常意味着某些东西是错误的,各种不确定的行为可能很快就会让你感到厌烦.另一方面,如何通过写出它的定义来使lambda 更加定义?

c++ lambda templates c++17

31
推荐指数
1
解决办法
1061
查看次数

long double(特定于GCC)和__float128

我正在寻找的详细信息long double,并__float128在GCC/86(更多的是出于好奇不是因为的实际问题).

很少有人可能会需要这些(我只是,这是第一次真正需要的double),但我想你知道你的工具箱里有什么以及它的含义仍然值得(而且很有趣).

有鉴于此,请原谅我有些开放的问题:

  1. 有人可以解释这些类型的实现原理和预期用法,也可以相互比较吗?例如,它们是"尴尬实施",因为标准允许类型,有人可能会抱怨,如果它们只是相同的精度double,或者它们是否属于一流类型?
  2. 或者,有人有一个好的,可用的网络参考分享?谷歌搜索"long double" site:gcc.gnu.org/onlinedocs没有给我很多真正有用的东西.
  3. 假设常见的口头禅"如果你认为你需要加倍,你可能不理解浮点"不适用,即你真的需要更多的精度而不仅仅是float,并且不关心是8或16字节的内存是烧毁...是合理的期望,人们可以也只是跳long double__float128代替double无显著的性能影响?
  4. 当值在内存和寄存器之间移动时,英特尔CPU的"扩展精度"功能一直是令人讨厌的惊喜的源头.如果实际存储了96位,则该long double类型应该消除此问题.另一方面,我理解long double类型是互斥的-mfpmath=sse,因为SSE中没有"扩展精度"这样的东西.__float128另一方面,SSE数学应该可以完美地工作(尽管没有四分之一精度指令肯定不适用于1:1指令库).我对这些假设是对的吗?

(3.和4.可能可以通过花费在分析和反汇编上的一些工作来解决,但也许其他人之前有过相同的想法,并且已经完成了这项工作.)

背景(这是TL; DR部分):
我最初绊倒了long double,因为我一直在寻找起来DBL_MAX<float.h>,并incidentially LDBL_MAX是在下一行."哦,看,GCC实际上有128位双打,不是我需要它们,但是......很酷"是我的第一个念头.惊喜,惊喜:sizeof(long double)回报12 ......等等,你的意思是16?

毫不奇怪,C和C++标准没有给出类型的非常具体的定义.C99(6.2.5 10)表示数字doublelong doubleC++ 03状态(3.9.1 8)的一个子集,long double其精度至少与double(同样的,只是措辞不同)相同.基本上,标准把一切的实现,以同样的方式与long, …

gcc long-double

28
推荐指数
2
解决办法
2万
查看次数

未定义行为与错误形成之间的区别,无需诊断消息

C++标准配备的用于定义一个惊人的数不清楚1种,其意味着与细微的差别或多或少相同的行为.读到这个答案后,我注意到"该程序格式错误;无需诊断"的措辞.

实现定义未指定的行为的不同之处在于前一种情况下的实现必须清楚地记录它正在做什么(在后一种情况下,它不需要),两者都是格式良好的.未定义的行为与未指定的行为不同,因为程序是错误的(1.3.13).
否则它们都有一个共同点,就是标准不会对实现的内容做出任何假设或要求.除1.4/8之外,其中声明实现可能具有不会改变格式良好的程序的行为的扩展,但根据标准是不正确的,并且实现必须诊断使用这些,但之后可以继续编译和执行不正当的计划.

一个病态的程序否则只能定义为没有很好地形成(太棒了!).甲合式程序,在另一方面,被定义为一个附着在语法和诊断的语义规则.因此,这意味着错误的程序是打破语法或语义规则(或两者)的程序.换句话说,一个不正确的程序实际上根本不应该编译(如何以任何有意义的方式翻译例如具有错误语法的程序?).

我倾向于认为错误这个词也意味着编译器应该使用错误消息中止构建(毕竟,错误表明存在错误),但1.3.13中的"注意"部分明确允许不同的东西,包括默默地忽略问题(并且编译器显然不会因为UB 破坏构建,大多数甚至不会默认警告).

人们可能会进一步认为错误和不正确的形式是相同的,但如果情况或该词应该是什么意思,那么标准就不会详细说明.

此外,1.4表示

符合要求的实施应[...]接受并正确执行格式良好的程序

如果程序包含违反不需要诊断的规则,则不要求对该程序进行实施.

换句话说,符合要求的实现必须接受格式良好的程序,但它也可能接受形式错误的程序,甚至没有警告.但是,如果程序因为使用扩展而格式不正确.

第二段建议任何与"无需诊断"相关的内容意味着规范中没有要求,这意味着它大部分等同于"未定义的行为",除非没有提到错误.

因此,使用"形成不良;无需诊断"等措辞背后的意图是什么?

"无诊断"的存在表明它与未定义的行为完全相同(或大部分相同?).此外,由于实现定义未指定的行为被定义为格式良好,因此它必须是不同的.

另一方面,由于格式错误的程序会破坏语法/语义规则,因此它实际上不应该编译.但是,与"不需要诊断"相结合意味着允许编译器在没有警告的情况下以静默方式退出,并且之后您将无法找到可执行文件.

"形成不良;无需诊断"和"未定义行为"之间是否存在差异,或者这只是同一事物的复杂同义词?


1对行为集体缺乏更好的措辞

c++ undefined-behavior language-lawyer

23
推荐指数
2
解决办法
3409
查看次数

微小读取(重叠,缓冲)的解释优于大型连续读取?

(道歉有些冗长的介绍)

在此期间,prefaults整个大文件(> 400MB)到缓冲区缓存后加快了实际运行的应用程序的开发,我测试是否每次读4MB仍然有超过一次读取只有1MB块任何明显的好处.令人惊讶的是,较小的请求实际上变得更快.这似乎违反直觉,所以我进行了更广泛的测试.

缓冲区缓存在运行测试之前被清除(只是为了笑,我也在缓冲区中运行了一个文件.无论请求大小如何,缓冲区缓存都能提供高达2GB/s的速度,但令人惊讶的是+/- 30%随机方差).
使用的所有读取都与相同的目标缓冲区重叠ReadFile(使用FILE_FLAG_OVERLAPPED 使用句柄打开FILE_FLAG_NO_BUFFERING).使用的硬盘有点老,但功能齐全,NTFS的簇大小为8kB.初始运行后磁盘进行了碎片整理(6个碎片与未碎片,零差异).为了更好的数字,我也使用了更大的文件,下面的数字是读取1GB.

结果真的令人惊讶:

4MB x 256    : 5ms per request,    completion 25.8s @ ~40 MB/s
1MB x 1024   : 11.7ms per request, completion 23.3s @ ~43 MB/s
32kB x 32768 : 12.6ms per request, completion 15.5s @ ~66 MB/s
16kB x 65536 : 12.8ms per request, completion 13.5s @ ~75 MB/s
Run Code Online (Sandbox Code Playgroud)

因此,这表明提交数千个请求两个簇的长度实际上比提交几百个大的连续读取更好.提交时间(ReadFile返回之前的时间)确实随着请求数量的增加而上升,但异步完成时间几乎减半.
在每种情况下,内核CPU时间大约为5-6%(在四核上,所以应该说20-30%),而异步读取正在完成,这是一个惊人的CPU数量 - 显然操作系统做了一些非也是无比的忙碌等待.在2.6 GHz时,30%的CPU持续25秒,这是"无所事事"的相当多的周期.

知道如何解释这个吗?也许这里有人对Windows重叠IO的内部工作有更深入的了解?或者,您是否可以使用ReadFile读取兆字节的数据?

我可以看到IO调度程序如何通过最小化搜索来优化多个请求,尤其是当请求是随机访问时(它们不是!).我还可以看到,在NCQ中给出一些请求,硬盘如何能够执行类似的优化.
然而,我们谈论的是荒谬的一些荒谬的小要求 - 尽管如此,它们的表现仍然超过2倍的合理要求.

旁注:明显的赢家是内存映射.我几乎倾向于添加"毫不奇怪",因为我是内存映射的忠实粉丝,但在这种情况下,它实际上感到惊讶,因为"请求"甚至更小,操作系统应该更不能预测和安排IO.我最初没有测试内存映射,因为它似乎反直觉,甚至可以远程竞争.那么多你的直觉,嘿.

在不同偏移处重复映射/取消映射视图几乎为零时间.使用16MB视图并使用简单的for()循环对每个页面进行错误操作,每页读取一个字节,在9.2秒内完成@~111 …

windows winapi buffered overlapped-io

18
推荐指数
1
解决办法
1703
查看次数

Java fork/join框架逻辑

这对于今天另一个问题的回答是一个"副作用" .这更多是关于好奇心而不是实际问题.

Java SE 7提供了Oracle称之为"fork/join框架"的东西.这是将工作安排到多个处理器的一种可能的优秀方式.虽然我理解它应该如何工作,但我无法理解它优越的地方和关于偷工作的说法.

也许其他人更深入地了解为什么这种方法是可取的(除了因为它有一个奇特的名字).

/加盟叉的根本原语ForkJoinTasks,这是FutureS,而这个想法是要么执行工作立即[原文](措辞是误导,因为"立即"意味着它同步发生在主线程,在现实中发生这种情况的内部a Future)低于某个阈值递归地将工作划分为两个任务,直到达到阈值.

未来是一种封装任务的概念,该任务以不透明和未指定的方式异步运行到对象中.您有一个函数可以验证结果是否可用,并且您获得了一个允许您(等待和)检索结果的函数.
严格地说,你甚至不知道未来是否异步运行,它可以在内部执行get().从理论上讲,实现可以为每个未来生成一个线程或使用线程池.
实际上,Java将future作为任务队列上的任务实现,并附加了一个线程池(对于整个fork/join框架也是如此).

fork/join文档给出了这个具体的用法示例:

protected void compute() {
    if (mLength < sThreshold) {
        computeDirectly();
        return;
    }

    int split = mLength / 2;

    invokeAll(new ForkBlur(mSource, mStart, split, mDestination),
              new ForkBlur(mSource, mStart + split, mLength - split,
                           mDestination));
}
Run Code Online (Sandbox Code Playgroud)

这将以与Mergesort将如何遍历它们的方式相同的方式将任务提交给底层线程池的任务队列(由于递归).
比如说我们有一个32个"项目"的数组要处理并且阈值为4,并且均匀分割,它将产生8个任务,每个具有4个"项目",看起来像这样:

00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 …
Run Code Online (Sandbox Code Playgroud)

java concurrency multithreading java.util.concurrent fork-join

14
推荐指数
1
解决办法
4124
查看次数

为什么GCC std :: atomic增量会产生低效的非原子组装?

__sync_fetch_and_add使用自己的atomic模板已经使用gcc的Intel兼容内置(如)了很长一段时间." __sync"功能现在被正式视为"遗产".

C++ 11支持std::atomic<>及其后代,因此使用它似乎是合理的,因为它使我的代码符合标准,并且编译器将以平台无关的方式生成最佳代码,这几乎太好了真正.
顺便说一下,我也只需atomic要用文字替换std::atomic.有很多的std::atomic(重:内存模型),我真的不需要,但默认参数采取照顾.

现在是坏消息.事实证明,从我所知道的,生成的代码是......彻底的废话,甚至根本不是原子的.即使是增加一个单个原子变量并把它输出具有不少于5个非内联函数调用的最小例子___atomic_flag_for_address,___atomic_flag_wait_explicit__atomic_flag_clear_explicit(完全优化),并且在另一方面,没有在所生成的可执行的单个原子指令.

是什么赋予了?当然总是存在编译器错误的可能性,但是对于大量的审阅者和用户来说,这种相当激烈的事情通常不会被忽视.这意味着,这可能不是一个错误,而是预期的行为.

这么多函数调用背后的"基本原理"是什么,以及如何在没有原子性的情况实现原子性?

尽可能简单的例子:

#include <atomic>

int main()
{
    std::atomic_int a(5);
    ++a;
    __builtin_printf("%d", (int)a);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

产生以下内容.s:

movl    $5, 28(%esp)     #, a._M_i
movl    %eax, (%esp)     # tmp64,
call    ___atomic_flag_for_address   #
movl    $5, 4(%esp)  #,
movl    %eax, %ebx   #, __g
movl    %eax, (%esp)     # __g,
call    ___atomic_flag_wait_explicit     #
movl    %ebx, …
Run Code Online (Sandbox Code Playgroud)

gcc atomic c++11

11
推荐指数
1
解决办法
3007
查看次数

中毒某些API函数(不是维护者)

与例如: 如何在VC++中毒化标识符?
在C++中"毒化一个函数"是什么意思?

有没有办法"正确"毒化一些声明(或实现)不受我控制的函数?

具体来说,我试图阻止某些Windows API函数的使用可能导致意外结果,例如CreateFileA(使用T宏隐藏了混合ANSI和Unicode的事实)或SetWindowLong(这将导致您的程序无声地失败,没有错误,也没有机会知道64位系统出了什么问题).

我对"正确"中毒的定义是,试图调用该函数会破坏构建时出现可读错误(如果错误发生在源代码中的正确位置,则奖励积分).
优选地,这应该是便携式的,并且至少它必须与GCC和clang一起工作.

到目前为止我尝试/看过的内容:

预处理器

最简单,最明显的解决方案是利用预处理器:

#define CreateFileA __poison_ANSI_CreateFileA
Run Code Online (Sandbox Code Playgroud)

这对于一个特定的函数以及其他几个函数非常有效,完全按照预期工作,具有提示问题的精确错误并指向源中的正确位置:

error: no matching function for call to '__poison_ANSI_CreateFileA'
note: expanded from macro 'CreateFile'
note: expanded from ... (precise location in source)
Run Code Online (Sandbox Code Playgroud)

对于不同的功能,每个人都需要有一个唯一的名称,以避免冲突的定义错误,这是繁琐但容易完成的.到目前为止一切那么好,除了它不适用于MinGW-w64标头本身使用可变参数宏篡改名称的函数,例如CreateWindowSetWindowLong.无论如何,这些编译得很好,中毒与否.

编译

#pragma GCC poison铿锵碰巧也理解了(并且还有自己的版本),外观和声音应该完全符合我的要求,并以最惯用的方式.唉,它不起作用.或者说,它运作得太好了.
当中毒名称出现在声明中时,使用pragma指令中毒的函数已经触发错误,这意味着... 每次都在包含标题的每个构建中.这对编写程序没有多大帮助!

属性

这些都有一个巨大的缺点,你必须完全复制类型定义,以免在合法的调用上出现"模糊函数调用"错误.另一方面,它们不起作用.clang抱怨__attribute__((__error__))但忽略gnu::__error__gnu::error同样gnu::deprecated.此外,标准[[deprecated]]都很好,但似乎没有什么(编译好,甚至没有警告?!).

有什么我能做的吗?

c++

11
推荐指数
1
解决办法
262
查看次数

隐式内部联系与显式内部联系("静态")不同?

今天我遇到了一种古怪的东西,虽然可能不是很重要,但仍困扰着我.也许我只是没有正确理解C++.

源文件中的某些数组指向字符串文字,如下所示:

const char* a[] = { "a", "b", "c" };
const char* b[] = { "d", "e"};
const char* c[] = { "f", "g"};
Run Code Online (Sandbox Code Playgroud)

除了传递给GetProcAddress从库中检索函数指针(这是一个非阻塞的动态OpenAL/EFX /捕获函数加载器和上下文创建者/管理器)之外,这些指针数组都不会以任何方式使用.

最终我发现我应该声明这些变量,static const因为它们在.cpp文件之外的任何地方都不需要,所以使内部链接显式是合适的.无论如何,它们应该有内部联系(ISO14882 3.5(3)),所以我们只是通过明确编译器已经假定的内容来成为好公民.

进行无辜的更改导致可执行文件大小增加512字节.不像额外的512b真的很重要,但它似乎没有意义,完全相同的事情会导致不同的代码.由于static const不推荐使用(ISO14882 7.3.1.1(2)),我也尝试了一个匿名命名空间,结果相同.

查看汇编程序源显示显式内部链接(staticnamespace{})将字符串文字移动到.rdata而不是.data,并且字符串文字与指向字符串文字数组交错,而不是在一个块中包含所有字符串和所有指针,分别.这里可能存在不同大小的原因 - 很可能将数据从一个部分改组到另一个部分已经达到了部分大小约束.有趣的是,所有3种口味的名称也不同.

现在我想知道:如果那些指针没有内部联系,我是否会犯谬误?

另外,在我的理解const中已经是只读的,inhowfar是static const"更多只读"(一个进入.rdata而另一个没有)?

c++ static gcc const linkage

9
推荐指数
1
解决办法
454
查看次数

在C++ 11中使函数模板参数无符号

在模板函数中,如下所示:

template<typename T> constexpr T foo(T a, T b) { return /*recursive call*/; }
Run Code Online (Sandbox Code Playgroud)

我收到一个关于比较已签名与未签名(由于比较sizeof)我要消除的警告.

从概念上讲,人们需要这样的东西:

template<typename T> constexpr T foo(T a, unsigned T b) { ... }
    or
template<typename T> constexpr T foo(T a, std::make_unsigned<T>::type b) { ... }
Run Code Online (Sandbox Code Playgroud)

不幸的是,第一个版本不是有效的C++,第二个版本打破了构建,因为在编译器看到时T不是限定类型make_unsigned.

是否有解决方案实际上有效?

(注意:以某种方式与获取整数模板参数的有符号/无符号变体没有显式特征相关/几乎相同,虽然函数而不是类(因此没有typedef),特征或C++ 11的任何特性明确欢迎,并且工作解决方案(即不是 make_unsigned<T>)首选.)

c++ templates c++11

9
推荐指数
1
解决办法
1272
查看次数

用户定义文字的每个“正常”使用都是未定义的行为吗?

用户定义的文字必须以下划线开头。

这是一个或多或少众所周知的规则,您可以在每个谈论用户文字的外行网站上找到。这也是我(可能还有其他人?)一直公然无视的规则,因为“真是胡说八道”。当然,这绝对是不正确的。在最严格的意义上,这使用了一个保留标识符,因此会调用未定义的行为(尽管实际上你不会从编译器那里得到那么多的耸肩)。

因此,考虑我是否应该继续故意忽略标准的(在我看来无用的)部分,我决定查看实际编写的内容。因为,你知道,每个人都知道什么有什么关系。重要的是标准中写了什么。

[over.literal]指出“一些”字面后缀标识符是保留的,链接到[usrlit.suffix]. 后者声明所有都是保留的,除了以下划线开头的那些。好的,这几乎就是我们已经知道的,明确写的(或者更确切地说,是倒着写的)。

此外,[over.literal]包含一个提示,提示一个明显但令人不安的事情:

除了上面描述的约束,它们都是普通的命名空间范围函数和函数模板

好吧,他们肯定是。没有任何地方说它们不是,那么您还期望它们是什么。

但请稍等。[lex.name]明确声明全局命名空间中以下划线开头的每个标识符都是保留的。

现在,一个文字操作符通常,除非你明确地把它放入一个命名空间(我相信没有人这样做!?)在全局命名空间中非常多。因此,必须以下划线开头的名称是保留的。没有提到特殊的例外。因此,每个名称(带下划线或不带下划线)都是保留名称。

您是否确实希望将用户定义的文字放入命名空间,因为“正常”用法(下划线与否)使用的是保留名称?

c++ language-lawyer

9
推荐指数
4
解决办法
342
查看次数