考虑以下代码:
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模式下不执行任何此类优化.
为了使语法更简洁,我想使用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) 以下是我正在使用的迭代器的简化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必须在继续之前将所有定义添加到其符号表中.标准是指定一种方法还是另一种方法,或者该实现是否具体?也就是说,如果定义不在命名空间内,我不明白为什么这不是问题.
在查找NaN运行我的代码的优化版本(编译g++ 4.8.2和4.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)