Я собрал упрощенный набор тестовых примеров, которые делают эту проблему очевидной. Здесь Array и Map не являются шаблонными, а представляют собой простые структуры, для простоты основанные на std::vector и std::map. Это по-прежнему ясно показывает, что тип категории сравнения должен быть известен заранее. Или оператор придется вообще исключить, но есть недостатки (доступно на godbolt.com).
Код: Выделить всё
#include
#include
#include
#include
#include
/// Set 1-8, to choose between cases
#define CASE 1
#define WANT_DEFAULT_WHEN_MEMEBER 0
/// Must be forward declared, as, e.g., using `std::vector` in Var's declaration would not be possible
struct Array;
struct Map;
using Var = std::variant;
/// [CASE 1] Deduced comparison category type + default
/// [COMPILES] No compiler
#if CASE == 1
struct Array
{
bool operator==(const Array & that) const = default;
auto operator(const Array & that) const = default;
std::vector value;
};
struct Map
{
bool operator==(const Map & that) const = default;
auto operator(const Map & that) const = default;
std::map value;
};
/// [CASE 2] Deduced comparison category type, declared outside, still default
/// [COMPILES] No compiler
#elif CASE == 2
struct Array
{
bool operator==(const Array & that) const = default;
auto operator(const Array & that) const;
std::vector value;
};
struct Map
{
bool operator==(const Map & that) const = default;
auto operator(const Map & that) const;
std::map value;
};
auto Array::operator(const Array & that) const = default;
auto Map::operator(const Map & that) const = default;
/// [CASE 3] Declared outside, with concrete comparison category type
/// [COMPILES] Only in GCC 15.2
#elif CASE == 3
struct Array
{
bool operator==(const Array & that) const = default;
std::weak_ordering operator(const Array & that) const;
std::vector value;
};
struct Map
{
bool operator==(const Map & that) const = default;
std::weak_ordering operator(const Map & that) const;
std::map value;
};
std::weak_ordering Array::operator(const Array & that) const
{
return value that.value;
}
std::weak_ordering Map::operator(const Map & that) const
{
return value that.value;
}
/// [CASE 4] Template hack to delay instantiation, deduced comparison category type
/// [COMPILES] Only MSVC 19
#elif CASE == 4
struct Array
{
bool operator==(const Array & that) const;
template
auto operator(const T & that) const;
std::vector value;
};
struct Map
{
bool operator==(const Map & that) const;
template
auto operator(const T & that) const;
std::map value;
};
template
auto Array::operator(const T & that) const
{
return value that.value;
}
template
auto Map::operator(const T & that) const
{
return value that.value;
}
bool Array::operator==(const Array & that) const
{
return value == that.value;
}
bool Map::operator==(const Map & that) const
{
return value == that.value;
}
/// [CASE 5] Template hack to delay instantiation, concrete comparison category type
/// [COMPILES] All compilers
/// [LIMITATION] Requires knowing comparison category type, won't work if deducation is needed (template class)
#elif CASE == 5
struct Array
{
bool operator==(const Array & that) const;
template
std::weak_ordering operator(const T & that) const;
std::vector value;
};
struct Map
{
bool operator==(const Map & that) const;
template
std::weak_ordering operator(const T & that) const;
std::map value;
};
template
std::weak_ordering Array::operator(const T & that) const
{
return value that.value;
}
template
std::weak_ordering Map::operator(const T & that) const
{
return value that.value;
}
bool Array::operator==(const Array & that) const
{
return value == that.value;
}
bool Map::operator==(const Map & that) const
{
return value == that.value;
}
/// [CASE 6] As friends, no template hacks, declared before the type is defined
/// [COMPILES] Only MSVC 19
/// [LIMITATION] Only as out of class friend (not critical)
#elif CASE == 6
struct Array
{
friend bool operator==(const Array & _1, const Array & _2);
friend auto operator(const Array & _1, const Array & _2);
std::vector value;
};
struct Map
{
friend bool operator==(const Map & _1, const Map & _2);
friend auto operator(const Map & _1, const Map & _2);
std::map value;
};
bool operator==(const Array & _1, const Array & _2)
{
return _1.value == _2.value;
}
auto operator(const Array & _1, const Array & _2)
{
return _1.value _2.value;
}
bool operator==(const Map & _1, const Map & _2)
{
return _1.value == _2.value;
}
auto operator(const Map & _1, const Map & _2)
{
return _1.value _2.value;
}
/// [CASE 7] As friends, no template hacks, declared before the type is defined, concrete comparison category type
/// [COMPILES] Only MSVC 19
/// [LIMITATION] Only as out of class friend (not critical)
/// [LIMITATION] Requires knowing comparison category type, won't work if deducation is needed (template class)
#elif CASE == 7
struct Array
{
friend bool operator==(const Array & _1, const Array & _2);
friend std::weak_ordering operator(const Array & _1, const Array & _2);
std::vector value;
};
struct Map
{
friend bool operator==(const Map & _1, const Map & _2);
friend std::weak_ordering operator(const Map & _1, const Map & _2);
std::map value;
};
bool operator==(const Array & _1, const Array & _2)
{
return _1.value == _2.value;
}
std::weak_ordering operator(const Array & _1, const Array & _2)
{
return _1.value _2.value;
}
bool operator==(const Map & _1, const Map & _2)
{
return _1.value == _2.value;
}
std::weak_ordering operator(const Map & _1, const Map & _2)
{
return _1.value _2.value;
}
/// [CASE 8] The full set of operators (pre C++20 way)
/// [COMPILES] All compilers
/// [LIMITATION] No `` therefore, when contained by another class, that class can't default its ``,
/// at least in clang, but others seem incorrect as there is no way to deduce comparison category type
/// I.e. won't work with `WANT_DEFAULT_WHEN_MEMEBER == 1`
#elif CASE == 8
struct Array
{
/// Can't be defaulted: some type would be forward declared at that point.
bool operator==(const Array & that) const;
bool operator
Подробнее здесь: [url]https://stackoverflow.com/questions/79896261/convoluted-operator-rules-in-case-of-mutually-recursive-stdvariant[/url]