如何避免意外地通过值而不是通过引用获取数组元素?

时间:2012-09-04 16:11:37

标签: c++ c++11 copy-constructor

假设我有一个数组std::vector<Foo>,我想迭代我所有的foos并按照这样做做事:

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

这最终无所事事,因为它正在制作vecFoo内容的本地副本而不是对它的引用。正确的循环如下:

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

这是我现在做过几次的错误,所以我想找到一个解决方案,当我弄错的时候会抓住我。对于我可以对结构做的事情或者我可以打开的警告标志,我会很高兴。我已经尝试将Foo的复制构造函数设为私有,但后来我最终无法执行push_backemplace_back,这显然不是我想要的。

3 个答案:

答案 0 :(得分:4)

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

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

尽管如此,这只是在这种特殊情况下触发错误的一种方式,但是如果您存储的任何类型不是100%在您的控制之下(即无法禁用复制构造或添加移动构造,或者无法实现nothrow移动构造函数 - 无法保证它不会抛出)然后你运气不好,这导致了第一句话:学会使用引用

答案 1 :(得分:1)

  

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

根据我的经验,最好的解决方案是训练自己默认将对象声明为const。这使得编译器检查错误以尝试修改它们。 一旦你这样做,当你忘记偏离它以使对象可写时,编译器将为你捕获这样的声明:

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

我总是这样写。然后,每当我在代码中看到以下内容时:

int y =  42;

我的思绪试图弄清楚变量是否应该实际上是可修改的。你可以将这种想法调整到你的循环示例中,因为谢天谢地,C ++ 11也允许这为range-for循环:

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

当然,只有在您自己训练自动const一切之后,这才有效。

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

答案 2 :(得分: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

请记住:如果您声明了复制构造函数和赋值运算符,即使只是删除它们,您必须手动声明 move 构造函数/赋值运算符。当然,您可以使用= default语法。