小编Bér*_*ger的帖子

重载operator []从1开始并且性能开销

我正在做一些C++计算机制(不用担心,这里不需要物理知识)并且有些东西真的困扰我.

假设我想表示一个3D数学向量(与std :: vector无关):

class Vector {
    public:
        Vector(double x=0., double y=0., double z=0.) { 
            coordinates[0] = x;
            coordinates[1] = y;
            coordinates[2] = z;
        }
    private:
        double coordinates[3];
};
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.现在我可以重载operator []来提取坐标:

double& Vector::operator[](int i) {
     return coordinates[i] ;
}
Run Code Online (Sandbox Code Playgroud)

所以我可以输入:

Vector V; 

… //complex computation with V

double x1 = V[0];
V[1] = coord2;
Run Code Online (Sandbox Code Playgroud)

问题是,从0开始索引在这里并不自然.我的意思是,在排序数组时,我并不介意,但事实是每篇论文,书籍或其他内容中的常规符号总是以1开头的子行程坐标.这可能看起来很狡辩,但事实是在公式中,它总是需要双重理解我们正在采取的措施.当然,对于矩阵来说,这是最糟糕的.

一个明显的解决方案是稍微不同的重载:

double& Vector::operator[](int i) {
     return coordinates[i-1] ;
}
Run Code Online (Sandbox Code Playgroud)

所以我可以输入

double x1 = V[1];
V[2] = coord2;
Run Code Online (Sandbox Code Playgroud)

它似乎是完美的,除了一件事:这个i-1减法似乎是一个很小的开销的好候选人.你会说很小,但我正在做计算机制,所以这通常是我们买不起的东西.

所以现在(最后)我的问题是:您认为编译器可以优化它,还是有办法使其优化?(模板,宏,指针或参考kludge ...)

逻辑上,在

double xi = V[i]; …
Run Code Online (Sandbox Code Playgroud)

c++ performance

6
推荐指数
2
解决办法
494
查看次数

将模板函数转换为通用lambda

我想传递模板化的函数,好像它们是通用的lambda,但是这不起作用.

#include <iostream>
#include <vector>
#include <tuple>
#include <string>
#include <utility> 


// for_each with std::tuple
// (from https://stackoverflow.com/a/6894436/1583122)
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT)
{}

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT f) {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
}

// my code
template<class T> auto
print(const std::vector<T>& v) -> void {
    for (const auto& e : …
Run Code Online (Sandbox Code Playgroud)

c++ lambda enable-if c++14

6
推荐指数
1
解决办法
403
查看次数

RVO何时应适用于/确实适用于C ++ 20编译器

C ++核心准则指出:

F.20:对于“输出”输出值,更喜欢返回值而不是输出参数

但是然后给出以下异常:

struct Package {      // exceptional case: expensive-to-move object
    char header[16];
    char load[2024 - 16];
};

Package fill();       // Bad: large return value
void fill(Package&);  // OK
Run Code Online (Sandbox Code Playgroud)

Isn't it supposed to be a case where the return value optimization kicks in ? Is RVO prevented in this case ? Or still not as efficient as passing by reference ? Or is it that some compilers don't manage to do it ?

More generally, when should I rely …

c++ compiler-optimization rvo c++20

6
推荐指数
1
解决办法
115
查看次数

在特定实现上定义了两个数组之间的指针差异吗?

根据C标准:

当两个指针相减时,两者都应指向同一数组对象的元素,或指向数组对象最后一个元素的元素(第 6.5.6 1173 节)

[注意:不要假设我对标准或 UB 了解很多,我只是碰巧发现了这个]

  1. 我知道在几乎所有情况下,无论如何取两个不同数组中指针的差异都是一个坏主意。
  2. 我也知道在某些架构上(我在某处读到的“分段机器”),行为未定义有充分的理由。

现在另一方面

  1. 它在某些极端情况下可能有用。例如,在这篇文章中,它将允许使用具有不同数组的库接口,而不是复制一个数组中的所有内容,然后将其拆分。
  2. 似乎在“普通”架构上,“所有对象都存储在一个大数组中,从大约 0 开始到大约内存大小”的思维方式是对内存的合理描述。当您实际查看不同数组的指针差异时,您会得到合理的结果。

因此我的问题是:从实验来看,似乎在某些体系结构(例如 x86-64)上,两个数组之间的指针差异提供了合理的、可重现的结果。它似乎与这些架构的硬件相当吻合。那么某些实现是否真的确保了特定的行为?

例如,在野外是否有一个实现可以保证a并且b存在char*,我们有a + (reinterpret_cast<std::ptrdiff_t>(b)-reinterpret_cast<std::ptrdiff_t>(a)) == b

c c++ pointer-arithmetic language-lawyer

6
推荐指数
3
解决办法
257
查看次数

将 end() 迭代器转换为指针

开门见山:以下安全吗?

vector<int> v;
int const* last = &*v.end(); 
// last is never dereferenced
Run Code Online (Sandbox Code Playgroud)

我担心的是,从迭代器获取普通旧指针的技巧会强制取消引用 end() 迭代器,这是无效的......即使只是将指针取回!

背景:我正在尝试创建由任意类型(尤其是整数和指向对象的指针)索引的条目集合。

template<class IT>
/// requires IT implements addition (e.g. int, random-access iterator)
class IndexingFamily {
    public:
        using IndexType = IT;

        IndexingFamily(IndexType first, IndexType last);
        int size() const;
        IndexType operator[](int i) const;
    private:
        IndexType first;
        IndexType last;
};

template<class IT> IndexingFamily<IT>::
IndexingFamily(IndexType first, IndexType last) 
    : first(first)
    , last(last) {}

template<class IT> auto IndexingFamily<IT>::
size() const -> int {
    return last-first;
}

template<class IT> …
Run Code Online (Sandbox Code Playgroud)

c++ pointers iterator

5
推荐指数
1
解决办法
1303
查看次数

编译器如何知道 C++ constexpr 计算不会触发未定义的行为?

C++ 标准要求编译器检查C++ constexpr 计算中未定义的行为

这次演讲中,Chandler Carruth 指出,在检查 UB 时“你将耗尽检测错误的能力”,并且在一般情况下,检测 UB 与停机问题有关,因此证明不可能做出决定

他不是在谈论 constexpr 中的 UB,但constexpr 计算与自 C++14 以来的常规程序一样通用,因此这仍然适用。

那么,当编译器无法确定程序是否是 UB 时,他们会做什么呢?他们是否仍然接受该程序并继续编译?或者他们是否更加保守并拒绝该计划,即使它可能是正确的?(我个人的感觉是他们这样做)

对我来说,这具有实际意义,因为我使用非平凡的指针算术进行了 constexpr 评估,用 Clang 编译得很好,但用 GCC 编译失败,而且我很确定这不是 UB。你可以说这是一个 GCC bug,但是如果 UB 是不可判定的,那么所有编译器在这方面都会存在 bug。

更根本的是,为什么标准要求不含 UB ?有技术原因吗?或者更多的是一种哲学(“如果编译器无法检查,程序员可以触发 UB,就会导致不好的结果”)?

我认为这与 C++ 的其余部分不一致,这永远不会阻止你搬起石头砸自己的脚。我希望 GCC 接受我的 constexpr 代码并崩溃,或者如果 UB 则发出垃圾;而不是在不知道是否是UB的情况下不编译。

====== 编辑======

正如 MM 和 Nicol Bolas 所指出的,该标准指定了限制(即使在 C++14 中也是如此),因此我们永远不会陷入 UB 的停止问题类型。然而,我仍然想知道检查 UB 是否可能太复杂,并且如果编译器启发式失败,那么它们会将其标记为非 constexpr(可能是错误的)。

但从评论中我感觉这更多是一个不成熟的实现问题。

c++ undefined-behavior language-lawyer constexpr

5
推荐指数
1
解决办法
312
查看次数

未知边界参数的多维数组:C 和 C++ 之间的区别

以下程序编译为 C 程序:

\n
#include <stdlib.h>\n#include <stdio.h>\n\nvoid f(int n, int m, int x[n][m]) {\n  printf("x[0][2] = %i\\n",x[0][2]);\n}\n\nint main() {\n  int v[][3] = { {0,1,2}, {3,4,5} };\n\n  f(2,3,v);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然而,当用 g++ 编译为 C++ 时,我有:

\n
main.c:4:29: error: use of parameter outside function body before \xe2\x80\x98]\xe2\x80\x99 token\n void f(int n, int m, int x[n][m]) {\n                             ^\n
Run Code Online (Sandbox Code Playgroud)\n

看来C的这个特性在C++中并不存在。是否可以向 g++ 提供任何标志以使其接受代码?

\n

c c++ gcc c99

5
推荐指数
1
解决办法
183
查看次数

使用Fortran的内存数据调用C代码

我有一个复杂的C++对象,我想在我的Fortran代码中使用它.通常,从Fortran调用C++代码没有问题(例如,只需要提供一个带有C链接的合适接口).

但是我的问题是我希望我对C++的Fortran调用能够操作我称之为持久对象的东西:由第一个init函数创建的C++对象,并由其他C++函数操作.

更具体地说,假设我有以下C++代码

struct A {
    public:
      void do() { // do something on complicated stuff
    private:
      ... // complicated stuff
};

extern "C" {
    void* init_A() {
         A* a = new A();
         return reinterpret_cast<void*>(a);
    }

    void doSth(void* ptr_to_A) {
         A* a = reinterpret_cast<A*>(ptr_to_A);
         a.do();
    }

    void teardown_A(void* ptr_to_A) {
         A* a = reinterpret_cast<A*>(ptr_to_A);
         delete a;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下fortran代码(假设它是main()):

USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
INTERFACE
    TYPE(C_PTR) FUNCTION init_A() BIND(C, NAME='init_A')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
        IMPLICIT NONE
    END …
Run Code Online (Sandbox Code Playgroud)

c c++ fortran memory-management fortran-iso-c-binding

3
推荐指数
1
解决办法
414
查看次数

完美的转发和构造函数

我试图理解完美转发和构造函数的相互作用.我的例子如下:

#include <utility>
#include <iostream>


template<typename A, typename B>
using disable_if_same_or_derived =
  std::enable_if_t<
    !std::is_base_of<
      A,
      std::remove_reference_t<B>
    >::value
  >;


template<class T>
class wrapper {
  public:
    // perfect forwarding ctor in order not to copy or move if unnecessary
    template<
      class T0,
      class = disable_if_same_or_derived<wrapper,T0> // do not use this instead of the copy ctor
    > explicit
    wrapper(T0&& x)
      : x(std::forward<T0>(x))
    {}

  private:
    T x;
};


class trace {
  public:
    trace() {}
    trace(const trace&) { std::cout << "copy ctor\n"; }
    trace& operator=(const …
Run Code Online (Sandbox Code Playgroud)

c++ temporary-objects perfect-forwarding

2
推荐指数
1
解决办法
345
查看次数