【问题标题】:Passing to a Reference Argument by Value按值传递给引用参数
【发布时间】:2016-10-24 08:39:42
【问题描述】:

考虑这个简单的程序:

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

问题当然是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++11 为我们提供了各种类型工具,我是否可以简单地将这个值强制为非引用类型并将该内联传递,而不创建临时的?

【问题讨论】:

  • 不是解决方案/答案,但您可以编写replace(begin(foo), end(foo), int(foo.front()), 13);,可以使用对象的复制构造函数进行模板化。我觉得你必须使用某种副本。
  • @thorsan,实际上,这是一个(有点骇人听闻的)解决方案,所以你也应该发布它。
  • @thorsan 不是我希望的答案,但我越看越喜欢它。我认为你可以提出一个可以辩护的案例,这将是最好和最简单的方法。如果你把它写下来作为答案,你至少会得到我的支持。
  • 您能以某种方式移动输入,因为您正在覆盖它吗?
  • @thorsan 不,在这种情况下我已将其简化为minimal, complete, verifiable example 我正在查看的情况可能来自范围内的任何地方并且我不知道在哪里。

标签: c++ c++11 reference pass-by-reference pass-by-value


【解决方案1】:

解决方案可能如下(即使您是临时的)

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);
}

【讨论】:

  • 正在讨论over here,但static_cast 在这里提供了一些额外的保护。虽然没有它这是一个很好的解决方案。
  • 嗯...我已更新 the linked question 来表达问题,但这不适用于 Visual Studio 2015。
  • 对于 Visual Studio 用户,您可以获得 C++11 合规性,因此无需修改单个项目即可支持:stackoverflow.com/q/37976395/2642059 但即使这是符合标准的,没有链接步骤,Visual Studio 2015 在这里将无法正常运行。
【解决方案2】:

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

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

【讨论】:

    【解决方案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 所建议的)只是一元 + 运算符,这是一个无操作:

    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';
    

    显然,其中任何一个仍然会创建一个临时的。我认为你无法摆脱这一点。

    【讨论】:

    • 是的,这样更好。更新答案
    • 甚至+foo.front()
    • 顺便说一句,您的解决方案在一般情况下不适用于任何T(至于std::string)。
    • @JonathanMee:这是一个无操作,并且不要更改值(即使是负数)。相当于他的+0
    • @JonathanMee 这实际上等同于 +0 实际上意味着 +int()
    【解决方案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;
     }
    

    ps:对不起,我的英语不好。

    【讨论】:

      【解决方案5】:

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

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

      而不是添加一个值。见Demo

      【讨论】:

      【解决方案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);
      

      【讨论】:

        【解决方案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})));
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-08
        • 1970-01-01
        • 2015-02-07
        • 2014-09-09
        • 2014-07-11
        • 2012-05-22
        • 1970-01-01
        相关资源
        最近更新 更多