小编And*_*der的帖子

编译器是否足够聪明,std :: move变量超出范围?

考虑以下代码:

std::vector<int> Foo() {
    std::vector<int> v = Bar();
    return v;
}
Run Code Online (Sandbox Code Playgroud)

return v是O(1),因为NRVO将省略副本,v 直接在存储中构造,否则将移动或复制函数的返回值.现在考虑功能类似的代码:

void Foo(std::vector<int> * to_be_filled) {
    std::vector<int> v = Bar();
    *to_be_filled = v;
}
Run Code Online (Sandbox Code Playgroud)

这里可以做出类似的论证,因为*to_be_filled = v它可以被编译成O(1)move-assign,因为它是一个超出范围的局部变量(编译器应该很容易验证v没有外部引用)这种情况,因此在最后一次使用时将其推广到rvalue).是这样的吗?有没有微妙的理由呢?

此外,感觉这种模式可以扩展到左值超出范围的任何上下文:

void Foo(std::vector<int> * to_be_filled) {
  if (Baz()) {
    std::vector<int> v = Bar();
    *to_be_filled = v;
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

do/can /是否有用/合理期望编译器找到诸如之类的模式*to_be_filled = v然后自动优化它们以假设rvalue语义?


编辑:

g ++ 7.3.0 在-O3模式下执行任何此类优化.

c++ rvalue compiler-optimization nrvo

7
推荐指数
1
解决办法
260
查看次数

作为 std::initializer_list 对象的抽象类

为了使语法更简洁,我想使用std::initializer_list将对象列表发送到构造函数。然而,这些对象是抽象的,这会导致一个问题:在 VS 2013 中,它丢失了 vfptr 引用,给出了“ R6025:纯虚函数调用”运行时错误,并且在 g++ 中它抱怨它“无法分配抽象对象在编译期间键入“base” ”。我推测编译器正在尝试复制对象(这是不可取的——它们可能很大),但只能成功复制基类,因此出现错误。我的问题是:是否有解决方案(1)避免复制对象并且(2)不是非常冗长,否定“更简洁的语法”优势?下面的代码说明了我的问题:

#include <cstdio>
#include <initializer_list>

struct base{
    virtual void foo() const = 0;
};

struct derived : public base{
    int i;
    derived(int i) : i(i) {}
    void foo() const{
        printf("bar %i", i);
    }
};

void foo_everything(const std::initializer_list<base> &list){
    for (auto i = list.begin(), iend = list.end(); i != iend; i++) i->foo();
}

int main(void){

    // Works fine
    derived d(0);
    base * base_ptr = &d;
    base_ptr->foo(); …
Run Code Online (Sandbox Code Playgroud)

c++ c++11

4
推荐指数
1
解决办法
650
查看次数

使用模板化类型和命名空间"无效使用不完整类型"

以下是我正在使用的迭代器的简化std::tuple.在其中,我在一个名称空间中定义模板化类型,并在第二个名称空间中使用它,如下所示:

namespace meta{
    template<size_t> struct meta_size_t {};     
}

namespace ns{

    //template<size_t> struct ns_size_t {};     

    template <size_t N> 
    void bar(meta::meta_size_t<N>) { 
        bar(meta::meta_size_t<N-1>()); 
    }
    void bar(meta::meta_size_t<1>) {}

    template<size_t N> 
    void foo() { 
        bar(meta::meta_size_t<N>()); 
    }
}

int main(void){
    ns::foo<5>();   
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

代码在MSVC2015中编译良好,但在g ++ 4.8和clang 3.5(-std = c ++ 11)中失败,达到模板的最大递归深度.请注意,如果meta::meta_size_t替换为ns_size_t (并且ns_size_t取消注释的定义),那么一切正常.

我猜测编译器会延迟解析,meta::meta_size_t直到它完成解析,bar因为它在另一个命名空间(或者这些行中的某些东西),因此失败了,但我不确定如何解决这个问题.

在什么条件下这个问题通常会出现?有没有办法迫使编译器namespace meta's之前解析内容ns's?我想避免重复类型定义(例如ns_size_t).

进一步的上下文:在原始代码中,bar有一个std::tuple参数,并使用调用重载函数std::get<N>(tuple)

编辑: Noobish错误.在查看了@WhozCraig提供的示例之后,我验证了代码可以通过交换两个bar方法的顺序来修复(我假设g ++和clang按顺序搜索定义,因此永远不会注册bar的第二个重载,而MSVC必须在继续之前将所有定义添加到其符号表中.标准是指定一种方法还是另一种方法,或者该实现是否具体?也就是说,如果定义不在命名空间内,我不明白为什么这不是问题.

c++ templates namespaces c++11

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

成功启用-fno-finite-math-only-NaN删除方法

在查找NaN运行我的代码的优化版本(编译g++ 4.8.24.9.3)时,一切都变为s 的错误,我发现问题是-Ofast选项,特别是-ffinite-math-only它包含的标志.

代码的一部分涉及从FILE*使用中读取浮点数fscanf,然后用NaN数值替换所有s.然而,正如可以预料的那样,-ffinite-math-only开始并删除这些检查,从而离开了NaNs.

在试图解决这个问题,我迷迷糊糊uppon 这个,这表明添加-fno-finite-math-only的方法属性禁用特定方法的优化.以下说明了问题和尝试修复(实际上没有修复它):

#include <cstdio>
#include <cmath>

__attribute__((optimize("-fno-finite-math-only"))) 
void replaceNaN(float * arr, int size, float newValue){
    for(int i = 0; i < size; i++) if (std::isnan(arr[i])) arr[i] = newValue;
}

int main(void){
    const size_t cnt = 10;
    float val[cnt];
    for(int i = 0; i < cnt; i++) scanf("%f", val + i);
    replaceNaN(val, cnt, -1.0f); …
Run Code Online (Sandbox Code Playgroud)

c++ gcc nan compiler-optimization

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

标签 统计

c++ ×4

c++11 ×2

compiler-optimization ×2

gcc ×1

namespaces ×1

nan ×1

nrvo ×1

rvalue ×1

templates ×1