clang能够将许多小整数的==比较转换为一条大SIMD指令,这让我很感兴趣,但是后来我注意到了一些奇怪的事情。当我进行7次比较时,Clang生成了“更差”的代码(在我的业余评估中),而当我进行8次比较时,Clang生成了该代码。
bool f1(short x){
return (x==-1) | (x == 150) |
(x==5) | (x==64) |
(x==15) | (x==223) |
(x==42) | (x==47);
}
bool f2(short x){
return (x==-1) | (x == 150) |
(x==5) | (x==64) |
(x==15) | (x==223) |
(x==42);
}
Run Code Online (Sandbox Code Playgroud)
我的问题是这是一个很小的性能错误,或者clang有一个很好的理由不想引入虚拟比较(即假装与7个值之一进行额外的比较),并在代码中使用一个更多的常量来实现它。
Godbolt链接在这里:
# clang(trunk) -O2 -march=haswell
f1(short):
vmovd xmm0, edi
vpbroadcastw xmm0, xmm0 # set1(x)
vpcmpeqw xmm0, xmm0, xmmword ptr [rip + .LCPI0_0] # 16 bytes = 8 shorts
vpacksswb xmm0, xmm0, xmm0
vpmovmskb eax, …Run Code Online (Sandbox Code Playgroud) 我正在观看“使用 C++20 三路比较 - Jonathan M\xc3\xbcller - Meeting C++ 2019”演讲,其中提到了包含浮点成员的类的问题。
\n\n问题源于这样一个事实:涉及 NaN 的 IEEE 754 比较很奇怪,并且不提供总排序。\nTalk 提供了一种解决此问题的方法,例如使用Strong_order或在实现 <=> 时手动忽略 NaN 值(假设值永远不是 NaN)。
\n\n我的问题是,是否有一些库包装器可以让我说“我保证”我的浮点数永远不是 NaN,或者可以对浮点数进行缓慢但有效的比较(更慢但更安全,因为 NaN 现在是有序的)。我的目标是通过使成员漂浮宇宙飞船友好来避免手动实现宇宙飞船(这样我可以默认宇宙飞船)。
\n\n使用演讲中的例子:
\n\n// original class\nstruct Temperature{\n double value;\n};\n\nstruct TemperatureNoNan{\n std::a_number<double> value; // I promise value will never be NaN\n // Now spaceship defaulting works\n};\n\nstruct TemperatureStrongO{\n std::s_ordered<double> value; // I want strong ordering(2 diff NaNs are not the same)\n // Now spaceship defaulting works\n};\nRun Code Online (Sandbox Code Playgroud)\n 这不是我懒得写
auto t = time(nullptr);
Run Code Online (Sandbox Code Playgroud)
而不是假设
auto t = time();
Run Code Online (Sandbox Code Playgroud)
我最感兴趣的是如果这是可能的,如果是(AFAIK它很容易实现,因为C++支持函数重载)为什么它没有完成。
我知道明显的答案,例如:使用<chrono>,没有人写过提案,但我想知道是否有不同的原因。
我最好的猜测是没有人想弄乱 C 库函数。
PS我知道有些人可能想关闭这个问题过于模糊,但我觉得可以对这个问题给出相对客观的答案。
考虑以下代码:
int main()
{
std::vector<std::chrono::steady_clock::time_point> time;
time.push_back(std::chrono::steady_clock::now());
std::this_thread::sleep_for(std::chrono::milliseconds(4));
time.push_back(std::chrono::steady_clock::now());
std::this_thread::sleep_for(std::chrono::milliseconds(7));
time.push_back(std::chrono::steady_clock::now());
std::vector<std::chrono::duration<double>> diffs;
std::adjacent_difference(time.begin(),time.end(),std::back_inserter(diffs));
}
Run Code Online (Sandbox Code Playgroud)
它无法编译(有关不匹配类型的丑陋模板错误消息)。当我尝试切换到输入错误消息 ( std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1, 1000000000>>>) 时,错误消息会四处移动。
我的假设是算法不起作用,因为减去 2 个时间点的结果不是时间点,即伪代码中的这行内容是冲突的。
template<class InputIt, class OutputIt>
constexpr // since C++20
OutputIt adjacent_difference(InputIt first, InputIt last,
OutputIt d_first)
{
if (first == last) return d_first;
typedef typename std::iterator_traits<InputIt>::value_type value_t;
value_t acc = *first;
*d_first = acc; // <----------------------------------------------------- 1
while (++first != last) {
value_t val = *first;
*++d_first = val - std::move(acc); // std::move since C++20 …Run Code Online (Sandbox Code Playgroud) 我试图fn根据某个constexpr值选择一个成员。然后我尝试调用选定的函数,但是我收到了关于如何fn使用不正确的语法调用成员的错误。
error: must use '.*' or '->*' to call pointer-to-member function in
'S::SelectedGetter<&S::fn1, &S::fn2>::fn (...)', e.g. '(... ->*
S::SelectedGetter<&S::fn1, &S::fn2>::fn) (...)'
18 | return SelectedGetter<&S::fn1, &S::fn2>::fn();
Run Code Online (Sandbox Code Playgroud)
我试图称之为“正确”但失败了。最后我使用的是std::invoke,但我想知道这是否可以在没有 的情况下完成std::invoke,只使用“原始”C++ 语法。
#include <algorithm>
#include <type_traits>
static constexpr int number = 18;
struct S
{
using GetterFn = uint32_t(S::*)() const;
uint32_t fn1()const {
return 47;
}
uint32_t fn2() const {
return 8472;
}
template <GetterFn Getter1, GetterFn Getter2>
struct SelectedGetter
{
static constexpr …Run Code Online (Sandbox Code Playgroud) 我知道我能做到
if (timeLeft.count() < 0)
Run Code Online (Sandbox Code Playgroud)
但我想知道最好的方法是什么,因为我也可以这样做:
if (timeLeft<std::chrono::seconds(0)) // or milliseconds or nanonseconds...
Run Code Online (Sandbox Code Playgroud)
注意:我认为这两项检查是相同的,但我不是计时专家。
编辑:完整示例:
#include<chrono>
int main(){
const std::chrono::nanoseconds timeLeft(-5);
if(timeLeft<std::chrono::seconds(0)){
return 47;
}
}
Run Code Online (Sandbox Code Playgroud)
edit2:潜在的问题std::chrono::seconds(0)是新手程序员可能会认为它涉及舍入,尽管事实并非如此。
即使使用 -fno-rtti 编译,它似乎在 GCC 和 Clang 中std::any 也能正常工作。
在查看 libc++ 源代码时,我发现他们只是使用了一个简单的技巧:
它们获取以任何类型为模板的变量的地址,这就是它们获取唯一 ID 的方式。
但此代码仅在没有打开 RTTI 时才有效。
这让我想知道。他们为什么首先使用 RTTI?为什么不总是使用这个解决方案呢?我不知道为什么typeid比简单的指针(指向每个类型实例化的静态变量)比较更快。
编辑:这与飞船无关。只是飞船的使用混淆了我代码中的真正问题(详见答案)。
我对这个程序的输出感到惊讶:(如果你喜欢谜题,请随意打开 Godbolt 链接并尝试自己找出原因)
#include <cstdint>
#include <cassert>
#include <compare>
#include <cmath>
#include <iostream>
#include <limits>
template<typename T>
struct TotallyOrdered
{
T val;
constexpr TotallyOrdered(T val) :
val(val) {}
constexpr operator T() const { return val; }
constexpr std::strong_ordering operator<=>(TotallyOrdered const& other) const
{
if (std::isnan(val) && std::isnan(other.val))
{
return std::strong_ordering::equal;
}
if (std::isnan(val))
{
return std::strong_ordering::less;
}
if (std::isnan(other.val))
{
return std::strong_ordering::greater;
}
if (val < other.val)
{
return std::strong_ordering::less;
}
else if (val == other.val)
{ …Run Code Online (Sandbox Code Playgroud) 受到可爱的 cppreference用 C++20 进行修剪的示例的启发,我编写了以下代码(我已将返回类型更改为void并将 arg 更改为std::string&,因为我的“问题”(我正在发明问题来学习 C++20)不存在于使用std::string_viewarg 并返回的原始代码std::string)。
void trim(std::string& in)
{
auto view
= std::views::all(in)
| std::views::drop_while(isspace)
| std::views::reverse
| std::views::drop_while(isspace)
| std::views::reverse;
std::string result{view.begin(), view.end()};
in = std::move(result);
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是这不是就位,这意味着创建了新字符串。我可以编写更丑陋的代码来就地执行此操作,并且我知道传统的 C++ 算法不知道容器的存在,但我想知道 C++20 是否有一些技巧使我能够以优雅的方式进行修剪,但也可以就地进行。
这也是我丑陋的就地修剪(不确定它是否正常工作,但想法是它可以就地修剪):
void trim2(std::string& s) {
// ugly and error prone, but inplace
const auto it1 = std::ranges::find_if_not(s, isspace);
const auto it2 = std::ranges::find_if_not(s.rbegin(), s.rend(), isspace);
const size_t shift = (it1==s.end()) ? 0: std::distance(s.begin(), it1); …Run Code Online (Sandbox Code Playgroud) 我有以下 C++ 程序,由于某种原因我不能int64_t用作模板参数。
#include <iostream>
#include <ranges>
template<typename T>
void fn() {
for (auto val : std::ranges::iota_view{T{1701}, T{8473}}
| std::views::reverse
| std::views::take(5))
{
std::cout << val << std::endl;
}
}
int main()
{
fn<int16_t>();
fn<int32_t>();
// does not compile:
// fn<int64_t>();
}
Run Code Online (Sandbox Code Playgroud)
这是预期的(我做错了什么),还是只是编译器/标准库中的一些不幸的错误?
注意:当我删除std::views::reverse代码时int64_t也编译。
c++ ×10
c++20 ×7
c++-chrono ×2
std-ranges ×2
assembly ×1
clang ×1
libc++ ×1
optimization ×1
rtti ×1
std ×1
std-invoke ×1
stdany ×1
templates ×1
x86 ×1