按值传递给引用参数

Passing to a Reference Argument by Value

提问人:Jonathan Mee 提问时间:6/22/2016 更新时间:6/22/2016 访问量:602

问:

考虑这个简单的程序:

vector<int> foo = {0, 42, 0, 42, 0, 42};
replace(begin(foo), end(foo), foo.front(), 13);

for(const auto& i : foo) cout << i << '\t';

当我写它时,我希望得到:

13 42 13 42 13 42

但相反,我得到了:

13 42 0 42 0 42

当然,问题在于它通过引用来获取最后 2 个参数。因此,如果它们中的任何一个恰好在正在操作的范围内,结果可能会出乎意料。我可以通过添加一个临时变量来解决这个问题:replace

vector<int> foo = {0, 42, 0, 42, 0, 42};
const auto temp = foo.front();
replace(begin(foo), end(foo), temp, 13);

for(const auto& i : foo) cout << i << '\t';

我确实知道 C++1 为我们提供了各种类型工具,我是否可以简单地将此值强制为非引用类型并在内联传递该值,而无需创建临时值?

C++ C++11 个引用 传递值

评论

8赞 thorsan 6/22/2016
不是解决方案/答案,但您可以使用对象的复制构造函数编写模板化。我觉得你必须使用某种副本。replace(begin(foo), end(foo), int(foo.front()), 13);
0赞 awesoon 6/22/2016
@thorsan,实际上,这是一个(有点黑客)的解决方案,所以你也应该发布它。
2赞 Jonathan Mee 6/22/2016
@thorsan 这不是我所希望的答案,但我越看越喜欢它。我认为你可以提出一个可辩护的案例,这将是最好和最简单的方法。如果你把它写成一个答案,你至少会得到我的支持。
0赞 thorsan 6/22/2016
你能以某种方式移动输入吗,因为你无论如何都在覆盖它?
1赞 Jonathan Mee 6/22/2016
@thorsan 不,在这种情况下,我已经简化了它,使其成为一个最小的、完整的、可验证的例子 我正在查看的案例可能来自该范围内的任何地方,我不知道在哪里。

答:

7赞 NathanOliver 6/22/2016 #1

您可以编写一个简单的函数来接受引用并返回一个值。这会将引用“转换”为值。这确实会生成一个临时的,但它是未命名的,将在完整表达式的末尾被销毁。类似的东西

template<typename T>
T value(const T& ref)
{
    return ref;
}

然后你可以像这样使用它

int main()                                                   
{                                                            
    vector<int> foo = {0, 42, 0, 42, 0, 42};
    replace(begin(foo), end(foo), value(foo.front()), 13);

    for(const auto& i : foo) cout << i << '\t';                                      
}

输出:

13  42  13  42  13  42

Live Example

9赞 thorsan 6/22/2016 #2

解决方案可以如下所示(即使您正在制作临时解决方案)

template<class T>
void replace_value_of_first(std::vector<T>& v, const T& value)
{
    std::replace(v.begin(), v.end(), T(v.front()), value);
}

评论

0赞 Jonathan Mee 6/22/2016
这里正在讨论它,但在这里提供了一些额外的保护。虽然这是一个很好的解决方案,但没有它。static_cast
0赞 Jonathan Mee 6/22/2016
嗯。。。我已经更新了链接的问题来表达问题,但这在 Visual Studio 2015 上不起作用。
0赞 Jonathan Mee 6/23/2016
对于 Visual Studio 用户,可以获得 C++11 合规性,因此无需修改单个项目即可支持此功能:For Visual Studio users you can get 11 compliance so this is supported without modified individual projects: stackoverflow.com/q/37976395/2642059 但是,即使这符合标准,如果没有链接的步骤,Visual Studio 2015 也无法在此处正常运行。
4赞 Smeeheey 6/22/2016 #3

您可以将给定的值转换为 以达到所需的效果。下面的示例无需定义任何额外的函数,只需将零添加到值中即可。rvalue

vector<int> foo = {0, 42, 0, 42, 0, 42};
replace(begin(foo), end(foo), foo.front()+0, 13);

for(const auto& i : foo) cout << i << '\t';

甚至(正如 Jarod42 所建议的)只是一元运算符,这是一个 no-op:+

vector<int> foo = {0, 42, 0, 42, 0, 42};
replace(begin(foo), end(foo), +foo.front(), 13);

for(const auto& i : foo) cout << i << '\t';

显然,这些中的任何一个仍然会产生暂时性。我不认为你能摆脱这一点。

评论

0赞 Smeeheey 6/22/2016
是的,那更好。更新的答案
3赞 Jarod42 6/22/2016
甚至.+foo.front()
3赞 Jarod42 6/22/2016
顺便说一句,您的解决方案在一般情况下不适用于任何(至于)。Tstd::string
1赞 Jarod42 6/22/2016
@JonathanMee:这是一个无操作,不要更改值(即使为负数)。它相当于他的.+0
1赞 W.F. 6/22/2016
@JonathanMee这实际上等同于手段+0+int()
1赞 max66 6/22/2016 #4

在这种特殊情况下(当旧值是向量的第一个值时),可以用 和 反转替换顺序。rbegin()rend()

总的来说,我不知道是否有可能,以一种简单的方式,不制作副本。

int main ()
 {
   std::vector<int> foo = {0, 42, 0, 42, 0, 42};
   std::replace(foo.rbegin(), foo.rend(), foo.front(), 13);

   for(const auto & i : foo)
      std::cout << i << '\t';

   std::cout << std::endl;

   return 0;
 }

p.s.:对不起,我的英语不好。

1赞 kwarnke 6/22/2016 #5

更明确地说,您可以用作构造函数来创建临时的:int()

replace(begin(foo), end(foo), int(foo.front()), 13);

而不是添加值。请参阅演示

评论

0赞 Jonathan Mee 6/22/2016
你来晚了一点:stackoverflow.com/a/37967761/2642059 但这是最好的解决方案。
1赞 Slava 6/22/2016 #6

一个应该适用于任何类型的衬里,而不仅仅是数字:

replace(begin(foo), end(foo), make_pair(foo.front(),0).first, 13);

或者不创建额外的字段:

replace(begin(foo), end(foo), get<0>( make_tuple(foo.front()) ), 13);
1赞 Glenn Downing 6/22/2016 #7
vector<int> foo = {0, 42, 0, 42, 0, 42};
replace(begin(foo), end(foo), static_cast<int>(foo.front()), 13);
assert(equal(begin(foo), end(foo), begin({13, 42, 13, 42, 13, 42})));

评论

0赞 Jonathan Mee 6/22/2016
哈哈:stackoverflow.com/questions/37969114/...事实证明,它在Visual Studio 2015上不起作用。