小编Dan*_*ica的帖子

std :: tuple sizeof,是否错过了优化?

我已经检查了所有主要的编译器,sizeof(std::tuple<int, char, int, char>)所有这些都是16。大概它们只是将元素按顺序放入元组,因此由于对齐而浪费了一些空间。

如果元组在内部像这样存储元素:int, int, char, char,则其sizeof可能为12。

实现有可能执行此操作,还是该标准中的某些规则禁止这样做?

c++ tuples padding language-lawyer

69
推荐指数
2
解决办法
2765
查看次数

为什么C++ 17中没有std :: construct_at?

C++ 17补充说std::destroy_at,但没有任何std::construct_at对应物.这是为什么?难道不能像下面这样简单地实现吗?

template <typename T, typename... Args>
T* construct_at(void* addr, Args&&... args) {
  return new (addr) T(std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

这样可以避免不完全自然的放置新语法:

auto ptr = construct_at<int>(buf, 1);  // instead of 'auto ptr = new (buf) int(1);'
std::cout << *ptr;
std::destroy_at(ptr);
Run Code Online (Sandbox Code Playgroud)

c++ placement-new c++17

58
推荐指数
4
解决办法
3055
查看次数

为什么GCC不能优化C++中空指针的删除?

考虑一个简单的程序:

int main() {
  int* ptr = nullptr;
  delete ptr;
}
Run Code Online (Sandbox Code Playgroud)

对于GCC(7.2),在结果程序中有call关于的指令operator delete.对于Clang和Intel编译器,没有这样的指令,空指针删除被完全优化(-O2在所有情况下).你可以在这里测试:https://godbolt.org/g/JmdoJi.

我想知道这样的优化能否以某种方式与GCC一起开启?(我更广泛的动机源于自定义swapvs std::swap可移动类型的问题,其中删除空指针可能代表第二种情况下的性能损失; 有关详细信息,请参阅/sf/answers/3198249771/.)

UPDATE

为了澄清我对这个问题的动机:如果我在移动赋值运算符和某个类的析构函数中使用delete ptr;if (ptr)保护,那么该类的对象将产生3个GCC指令.这可能是相当大的性能损失,例如,在对这些对象的数组进行排序时.std::swapcall

此外,我可以if (ptr) delete ptr;在任何地方写作,但是想知道,这是否也不能成为性能损失,因为delete表达式也需要检查ptr.但是,在这里,我想,编译器只会生成一个检查.

此外,我真的很喜欢delete没有后卫的可能性,这对我来说是一个惊喜,它可以产生不同的(表现)结果.

UPDATE

我只是做了一个简单的基准测试,即排序对象,它们delete在移动赋值运算符和析构函数中调用.来源是:https://godbolt.org/g/7zGUvo

std::sort使用GCC 7.1测量的运行时间和-O2Xeon E2680v3上的标志:

链接代码中存在一个错误,它会比较指针,而不是指向值.更正结果如下:

  1. 没有if后卫:17.6 [s] 40.8 [s], …

c++ null gcc compiler-optimization delete-operator

46
推荐指数
5
解决办法
5976
查看次数

当前的x86架构是否支持非临时负载(来自"正常"内存)?

我知道关于这个主题的多个问题,但是,我没有看到任何明确的答案或任何基准测量.因此,我创建了一个简单的程序,它使用两个整数数组.第一个数组a非常大(64 MB),第二个数组b很小,适合L1缓存.程序迭代a并将其元素添加到b模块化意义上的相应元素中(当到达结束时b,程序从其开始再次开始).测量的不同大小的L1缓存未命中数b如下:

在此输入图像描述

测量是在具有32 kiB L1数据高速缓存的Xeon E5 2680v3 Haswell型CPU上进行的.因此,在所有情况下,都b适合L1缓存.然而,大约16 kiB的b内存占用量大大增加了未命中数.这可能因为两者的负载预期ab导致缓存线失效从一开始b在这一点上.

绝对没有理由保留a缓存中的元素,它们只使用一次.因此,我运行一个具有非时间负载a数据的程序变体,但未命中数没有改变.我还运行了一个非暂时预取a数据的变体,但仍然有相同的结果.

我的基准代码如下(没有显示非时间预取的变体):

int main(int argc, char* argv[])
{
   uint64_t* a;
   const uint64_t a_bytes = 64 * 1024 * 1024;
   const uint64_t a_count = a_bytes / sizeof(uint64_t);
   posix_memalign((void**)(&a), 64, a_bytes);

   uint64_t* b;
   const uint64_t b_bytes = atol(argv[1]) * 1024;
   const uint64_t b_count = b_bytes …
Run Code Online (Sandbox Code Playgroud)

c c++ x86 caching prefetch

45
推荐指数
2
解决办法
1770
查看次数

为什么在析构函数可以不显式调用构造函数?

在下面的C++代码中,我可以显式调用析构函数而不是构造函数.这是为什么?不明确的ctor调用与dtor案件更具表现力统一性吗?

class X { };

int main() {
  X* x = (X*)::operator new(sizeof(X));
  new (x) X;  // option #1: OK
  x->X();     // option #2: ERROR

  x->~X();
  ::operator delete(x);
}
Run Code Online (Sandbox Code Playgroud)

c++ constructor destructor placement-new explicit-destructor-call

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

为什么std :: vector保留不会"加倍"它的容量,而resize呢?

我刚刚发现,std::vector<T>::resize即使调整大小超过当前大小的一个元素,它的容量"加倍":

std::vector<int> v(50);
v.resize(51);
std::cout << v.capacity() << std::endl;
Run Code Online (Sandbox Code Playgroud)

该程序使用GCC和Clang输出100,使用Visual C++输出75.但是,当我切换resizereserve:

std::vector<int> v(50);
v.reserve(51);
std::cout << v.capacity() << std::endl;
Run Code Online (Sandbox Code Playgroud)

所有三个编译器的输出为51.

我不知道为什么实现使用了不同的扩张策略resizereserve.它似乎不一致,我希望在这里有相同的行为.


我只是添加了一个关于我的问题动机的链接,其中报告了对性能的影响:为什么C++ STL向量在做多个预留时会慢1000倍?


添加C++ 11标准中的引用以阐明要求reserve; §23.3.6.3(2):

之后reserve(),capacity()大于或等于给的参数reserve,如果重新分配发生...


一些额外的想法:来自C++ 11标准:

复杂性:插入元素的数量加上到向量末尾的距离是复杂的.

实际上,这意味着在最后插入单个元素的常数(摊销)复杂性.但是,这仅适用于矢量修饰符,例如push_backinsert(§23.3.6.5).

resize未在修饰符中列出.它列在§23.3.6.3 vector容量部分中.并且,没有复杂性要求resize.

但是,在vector概述部分(§23.3.6.1)中,写有:

it(vector)支持(分期)常量时间插入和擦除操作

问题是是否resize(size()+1)被认为是"插入到最后".

c++ resize vector capacity

25
推荐指数
4
解决办法
2984
查看次数

在C++ 20中,不再允许在std中为程序定义类型专门化函数模板吗?

来自cppreference.com的报价:

添加模板特化

允许为任何标准库添加模板特化(从C++ 20开始)| 模板到命名空间std只有当声明依赖于至少一个程序定义的类型并且特化满足原始模板的所有要求时,除非禁止这样的特化.

是否意味着,从C++ 20开始,将std不再允许将函数模板的特化添加到用户定义类型的命名空间中?如果是这样,它意味着许多现有代码可能会破坏,不是吗?(在我看来,这是一种"激进"的改变.)此外,它会向这些代码注入未定义的行为,这不会触发编译错误(警告有希望).

c++ std template-specialization function-templates c++20

25
推荐指数
2
解决办法
1497
查看次数

为什么不使用`make_x()`函数尽可能省略移动构造函数?

我无法弄清楚为什么在最后一种情况下启用了复制省略时调用移动构造函数(甚至是强制性的,例如在C++ 17中):

class X {
  public:
    X(int i) { std::clog << "converting\n"; }
    X(const X &) { std::clog << "copy\n"; }
    X(X &&) { std::clog << "move\n"; }
};

template <typename T>
X make_X(T&& arg) {
  return X(std::forward<T>(arg));
}

int main() {
  auto x1 = make_X(1);    // 1x converting ctor invoked
  auto x2 = X(X(1));      // 1x converting ctor invoked
  auto x3 = make_X(X(1)); // 1x converting and 1x move ctor invoked
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,哪些规则阻碍了移动构造函数被省略?

UPDATE

调用移动构造函数时可能更直接的情况: …

c++ language-lawyer move-semantics copy-elision perfect-forwarding

21
推荐指数
2
解决办法
1250
查看次数

如何在C++中为MPL向量的所有成员显式实例化模板?

请考虑以下头文件:

// Foo.h
class Foo {
    public: 
        template <typename T>
        void read(T& value);
};
Run Code Online (Sandbox Code Playgroud)

我想Foo::read在源文件中显式实例化成员函数模板,包括在boost::mpl::vector:

// Foo.cc
#include <boost/mpl/vector.hpp>
#include <boost/mpl/begin_end.hpp>
#include "Foo.h"

template <typename T>
void Foo::read(T& value) { /* do something */ }

typedef boost::mpl::vector<int, long, float> types;

// template Foo::read<int  >(int&);
// template Foo::read<long >(long&);
// template Foo::read<float>(float&);

// instantiate automatically ???
Run Code Online (Sandbox Code Playgroud)

可能吗?谢谢,丹尼尔.

编辑

我找到了一些解决方案 - 似乎Foo::read<T>在结构的构造函数中指定一个指针,然后声明该变量,导致实例化:

// intermezzo
template <typename T> struct Bar {
    Bar<T>() {
        void (Foo::*funPtr)(T&) = &Foo::read<T>; …
Run Code Online (Sandbox Code Playgroud)

c++ boost vector boost-mpl explicit-instantiation

20
推荐指数
1
解决办法
2066
查看次数

C++中的标准或自定义异常?

对于库中的代码,是更好的做法是创建和抛出自定义异常类(库::例外),或只是抛出标准异常(runtime_error,invalid_argument等)?

c++ standards exception

19
推荐指数
2
解决办法
7140
查看次数