Мое лучшее предположение, что вы не можете избежать копирования здесь. Вся проблема сводится к следующему:
A x1;
std::tuple<A&> t1{x1};
const std::tuple<A>& t2{t1};
const std::tuple<A>& t3{std::tuple<A&>{x1}};
Обе конструкции t2
и t3
вызывают конструктор копирования A
(демонстрационная версия: https://wandbox.org/permlink/MxTUb61kO3zL3HmD).
Если вы действительно заботитесь о производительности, а экземпляры A
стоят дорого для копирования, вы можете поместить их в некоторый пул (например, std::vector<A>
), а затем поместить на карту только указатели на них (std::unordered_map<std::tuple<A*,A*>,B>
).
UPDATE
Обратите внимание, что вы также можете создать свой собственный класс "кортеж / пара", который может быть создан либо по значениям, либо по ссылкам. Очень простое решение может выглядеть так:
struct construct_from_ref_tag { };
template <typename T> class ref_pair {
public:
ref_pair(T v1, T v2)
: v1_(std::move(v1)), v2_(std::move(v2)), r1_(v1_), r2_(v2_) { }
ref_pair(const T& r1, const T& r2, construct_from_ref_tag)
: r1_(r1), r2_(r2) { }
bool operator==(const ref_pair<T>& rhs) const {
return ((r1_ == rhs.r1_) && (r2_ == rhs.r2_)); }
size_t hash() const {
return std::hash<T>{}(r1_) ^ std::hash<T>{}(r2_); }
private:
T v1_, v2_;
const T& r1_;
const T& r2_;
};
namespace std {
template <typename T> struct hash<ref_pair<T>> {
size_t operator()(const ref_pair<T>& v) const { return v.hash(); }
};
}
Его использование не вызывает конструктора копирования / перемещения при доступе к элементам на карте:
std::unordered_map<ref_pair<A>, int> map;
map[ref_pair<A>(1, 2)] = 3;
A a1{1};
A a2{2};
std::cout << "before access" << std::endl;
map[ref_pair<A>(a1, a2, construct_from_ref_tag{})] += 1;
Мне не очень нравится, но это работает. T
здесь должно быть конструируемым по умолчанию, а конструктор по умолчанию должен быть дешевым. Демонстрационная версия: https://wandbox.org/permlink/obSfPEJXn3Yr5oRw.