Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is unordered_set<reference_wrapper<Ty>> valid?

Is this valid C++ (considering the latest standard)? I'm getting compilation errors with near-top-of-tree clang/libc++ on Ubuntu 12.04. If it should be valid, I'll mail the clang-dev list with error messages and such.

#include <functional>
#include <unordered_set>

struct X
{
    int i; 
};

void f ()
{
    std::unordered_set<std::reference_wrapper<X>> setOfReferencesToX;

    // Do stuff with setOfReferencesToX
}

** As an aside, I'm tired of qualifying that the question/answer is specific to the latest standard. Could the C++ community as a whole, please start qualifying things that are specific to the old standard instead? The newer standard has been out for about a year now.

like image 654
Michael Price Avatar asked Aug 11 '12 15:08

Michael Price


People also ask

What is reference_wrapper?

A reference_wrapper<Ty> is a copy constructible and copy assignable wrapper around a reference to an object or a function of type Ty , and holds a pointer that points to an object of that type. A reference_wrapper can be used to store references in standard containers, and to pass objects by reference to std::bind .

What is unordered_set?

Unordered sets are containers that store unique elements in no particular order, and which allow for fast retrieval of individual elements based on their value. In an unordered_set, the value of an element is at the same time its key, that identifies it uniquely.

Can unordered_set have duplicate values?

Keys are immutable, therefore, the elements in an unordered_set cannot be modified once in the container - However, they can be inserted and removed. Unordered sets do not allow duplicates and are initialized using comma-delimited values enclosed in curly braces.

How is unordered_set implemented in C++?

An unordered_set is implemented using a hash table where keys are hashed into indices of a hash table so that the insertion is always randomized.


1 Answers

The problem is not specific to std::reference_wrapper<T>, but rather to the type X itself.

The issue is that std::unordered_set requires that you define hashing and equality functors for std::reference_wrapper<X>. You can pass the hash functor as second template parameter.

For example, this would work:

#include <functional> // for std::hash<int>

struct HashX {
  size_t operator()(const X& x) const {
    return std::hash<int>()(x.i);      
  }
};

and then

std::unordered_set<std::reference_wrapper<X>, HashX> setOfReferencesToX;

Another option is to make a specialization for std::hash<X>:

namespace std {
template <>
struct hash<X> {
  size_t operator()(const X& x) const {
    return std::hash<int>()(x.i);      
  }
};
}

This allows you to avoid explicitly specifying the 2nd template argument:

std::unordered_set<std::reference_wrapper<X>> setOfReferencesToX;

Concerning the equality comparison, you can fix this by providing an equality operator for class X:

struct X
{
  bool operator==(const X& rhs) const { return i == rhs.i; }
  int i; 
};

Otherwise, you can define your own functor and pass it as third template argument.

like image 66
juanchopanza Avatar answered Oct 04 '22 07:10

juanchopanza