Anonymous
Замысловатые правила оператора<=> в случае взаимно рекурсивного std::variant
Сообщение
Anonymous » 25 фев 2026, 16:53
Исходя из C++17 и пытаясь интегрировать оператор. Я наткнулся на загадку. Кажется, не существует способа реализовать универсальный контейнероподобный класс, где его T является взаимно рекурсивным std::variant. С обычным набором операторов сравнения до C++20 все в порядке, вероятно, потому, что они полностью определены в любой момент времени. Событие, если T заранее объявлено в точке объявления шаблона.
Я собрал упрощенный набор тестовых примеров, которые делают эту проблему очевидной. Здесь 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 Array & 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 Array & 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]
1772027580
Anonymous
Исходя из C++17 и пытаясь интегрировать оператор. Я наткнулся на загадку. Кажется, не существует способа реализовать универсальный контейнероподобный класс, где его T является взаимно рекурсивным std::variant. С обычным набором операторов сравнения до C++20 все в порядке, вероятно, потому, что они полностью определены в любой момент времени. Событие, если T заранее объявлено в точке объявления шаблона. Я собрал упрощенный набор тестовых примеров, которые делают эту проблему очевидной. Здесь Array и Map не являются шаблонными, а представляют собой простые структуры, для простоты основанные на std::vector и std::map. Это по-прежнему ясно показывает, что тип категории сравнения должен быть известен заранее. Или оператор придется вообще исключить, но есть недостатки (доступно на godbolt.com). [code]#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 Array & 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 Array & 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]