2012-09-04 20 views
2

说我有一个数组std::vector<Foo>,我想遍历我所有的Foo和做的东西做出来,像这样:如何避免意外地通过值而不是通过引用获取数组元素?

for (auto foo : vecFoo) 
    foo.x = 10; 

这结束了做什么,因为它使得vecFoo内容的本地副本,而不是对它的引用。正确的循环如下:

for (auto& foo : vecFoo) 
    foo.x = 10; 

这是我现在做了几次失误,所以我想为它找到一个解决方案,将赶上我,当我做错了。我会很满意我可以对结构做些什么或者可以打开一个警告标志。我试过使Foo的拷贝构造函数私有,但最终我无法做到push_backemplace_back,这显然不是我想要的。

+16

经验和肌肉记忆 –

+4

@DavidRodríguez-dribeas - 和单元测试。 –

+0

对于那些不记得在函数结尾删除它们的指针的人,我们有一些像scoped_ptr这样的东西,我正在为像我这样的人提出一些机制,这些机制并不总是记住循环中的小小&。机械解决方案会很好。 – Alex

回答

1

这是我现在做了几次的错误,所以我想找到一个解决方案,它会抓住我,当我发现它错了。

根据我的经验,最好的解决方案是训练自己总是默认声明对象为const。这使得它成为一个编译器检查错误,试图修改它们。 一旦你这样做,编译器会抓住这样的声明时,你已经忘记从中偏离使对象可写:

// This is what my hands write by default: 
int const x = 42; 

我尝试总是这样写。然后,每当我看到代码如下:

int y = 42; 

我的脑子里试图找出如果变量实际上应该修改。你可以因为这个令人欣慰的思想适应你的循环例如,C++ 11允许这为范围,对循环以及:

for (auto const i : vecFoo) 
    i.foo = 10; // error 

当然,这只是工作,一旦你已经训练自己自动const一切。

我同意这与完美相去甚远,需要大量的锻炼。正如几个人一再指出的那样,最好的解决方案是C++在默认情况下考虑声明const(事实上,至少对于lambda捕获的值,这个就是这种情况),但是该船已经航行了。

+3

我认为他_wants_'i.foo = 10'工作,但它不工作,因为他用'auto'制作了一个对象的副本,而不是'auto&'的一个参考。 –

+0

@Seth当然。但是,一旦你训练自己总是默认编写'const',那么编译器会在你忘记使对象成为一个(可变的)引用而不是*一个'const')时发出抱怨。这需要相当多的心理训练来养成一切,但我不知道更好的方法。查看更新。希望这使得它更清晰。是的,这不是一个特别好的解决方案。 –

+0

我想你误解了我的问题,这根本不能解决我的问题。问题不是常量,它是我的一个副本,而不是对vecFoo中的值的引用。 – Alex

4

潜在问题的答案是学习。如果再犯这样的错误,你最终会从经验中学习。

对于您的特殊情况,您可以完全控制存储在容器中的类型的语言技巧,您可以提供一个nothrow移动构造函数并禁用复制构造。这将需要使用emplace_back(或移动语义push_back)。

不过,这只是在这种特定情况下触发错误的一种方式,但如果您存储的任何类型不是您控制的100%(即,不能禁用拷贝构造或增加移动建设,还是无法实现抛出异常举动构造--cannot保证它不会抛出),那么你的运气了,这导致了第一句话:学会使用引用

+1

你可以包装这个类型! :) – Xeo

+0

@Xeo:这是一个有趣的想法。我不会这样做,但它仍然是一个有趣的想法。请注意,包装必须仍然提供相同的属性:没有复制构造函数(简单)和无法移动构造函数。第二个有点棘手。考虑到包装类型的拷贝构造函数可以抛出,那么你不能提供一个通过复制内部对象来实现* heavy *移动的包装器,所以你必须包装一个指针(这对于提供nothrow移动构造器来说是微不足道的) 。随后的应用程序更改内存布局,您将需要更新的代码... –

+0

...你最终打破了设计,因为你不希望有*记得*您是否要遍历一个容器复制或保存参考。你真的想要通过这个重构只是不必记得写一个&符号吗?我不。 –

1

我要求的是一种方法,使该副本对我的课程无效,以便没有犯这样的错误的机会。

C++ 11可以很容易地让你的类不可复制。只要删除拷贝构造函数/赋值运算符(假设你的编译器支持C++ 11功能):

class SomeClass 
{ 
public: 
    SomeClass(const SomeClass &) = delete; 
    SomeClass &operator=(const SomeClass &) = delete; 

... 
}; 

任何试图调用拷贝构造函数将产生一个编译器错误。如果您使用Visual Studio,但尚不支持此语法,则必须使用标准C++ 03成语。也就是说,私下声明拷贝构造函数。


我可以不再使用我的结构的载体,因为它不能被复制到载体。

当然可以。您只能使用需要复制的功能。您必须将push_backinsert的使用替换为emplace_backemplace

记住:如果你声明拷贝构造函数和赋值操作符,甚至只是删除它们,你必须手动声明举动构造函数/赋值运算符。当然,您可以使用= default语法。

+0

这似乎失败的原因是使复制构造函数私人失败,如问题中提到的:我不能再使用我的结构向量,因为它不能被复制到向量中。 – Alex

+0

@Alex:看我的编辑。 –

+0

因为我无法让它正常工作,所以我一定在做错了(或者有g ++中的错误)。 [这](http://pastebin.com/JCCPRjUK)不为我编译。我得到[这些错误](http://pastebin.com/527hZ4Cr)。 – Alex

相关问题