Issue with std::reference_wrapper

2019-01-27 13:15发布

问题:

The issue is clear with the following code:

#include <functional>
#include <iostream>
#include <vector>

int main() {
  //std::vector<int> a, b;
  int a = 0, b = 0;
  auto refa = std::ref(a);
  auto refb = std::ref(b);
  std::cout << (refa < refb) << '\n';
  return 0;
}

If I use the commented std::vector<int> a, b; instead of int a = 0, b = 0;, then the code does not compile on any of GCC 5.1, clang 3.6, or MSVC'13. In my opinion, std::reference_wrapper<std::vector<int>> is implicitly convertible to std::vector<int>& which is LessThanComparable, and thus it should be LessThanComparable itself. Could someone explain this to me?

回答1:

The issue is that the non-member operator< for std::vector is a function template:

template< class T, class Alloc >
bool operator<( const vector<T,Alloc>& lhs,
                const vector<T,Alloc>& rhs );

Implicit conversions are not considered when doing template type deduction here, [temp.arg.explicit] emphasis on if:

Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.

But in this case, the parameter type does participate in deduction. That's why it can't be found. Had we written our own non-template operator<:

bool operator<(const std::vector<int>& lhs, const std::vector<int>& rhs)
{
    return true;
}

Your code would work as expected. To use the generic one though, you will have to explicitly pull out the reference:

std::cout << (refa.get() < refb.get()) << '\n';


回答2:

Are you certain that

std::vector<int> a, b;

Is doing what it is supposed to? Take this for example

#include <functional>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> a, b;
  //int a = 0, b = 0;
  a.push_back(42);
  a.push_back(6);
  a.push_back(15);
  for (int ii=0; ii<43; ii++) {
    b.push_back(ii);
  }
  auto refa = std::ref(a);
  auto refb = std::ref(b);
  std::cout<<&refa<<std::endl;
  std::cout<<&refb<<std::endl;
  std::cout<<"Contents of vector A"<<std::endl;
  for(auto n : a)
  {
    std::cout<<' '<<n;
  }
  std::cout<<std::endl<<"Contents of vector b: ";
  for (auto n : b){
    std::cout<<' '<<n;
  }
  //std::cout << (refa < refb) << '\n';
  return 0;
}

Which results in

0x7fff5fbff0c0
0x7fff5fbff0b8
Contents of vector A
 42 6 15
Contents of vector b:  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

Ultimately

std::vector<int> a, b;

Creates two separate vectors of integers called a and b, both of which have no contents; this is not how one would declare a single vector with members a and b.

int a=0, b=0;

Declares two separate integers called a and b, which each have a value of 0. Those two code snippets declare completely different variables and should not be used interchangeably.