У меня есть структура, которая содержит несколько данных. Я хочу, чтобы доступ к наборам вычислялся во время компиляции. Для этого я создал шаблонный метод получения std::set для возврата запрошенного набора.
// simplified sample
struct TwoSets
{
template <int i> constexpr std::set<int> &get() noexcept
{
if constexpr (i == 0) {
return first;
} else if constexpr (i == 1) {
return second;
} else {
static_assert(i == 0 || i == 1, "invalid");
}
}
std::set<int> first;
std::set<int> second;
};
Это работает, но есть части кода, которые вставляются в заданный набор, и части кода, которые требуют доступа к набору только для чтения через ссылку constexpr на набор, например:
TwoSets sets;
sets.get<0>().insert(0);
// ...elsewhere in code
const TwoSets &const_sets = sets;
std::cout << const_sets.get<0>().size();
Это приводит к ошибке:
error: passing ‘const TwoSets’ as ‘this’ argument discards qualifiers [-fpermissive]
Это можно исправить, пометив const как get/вернув ссылку const, что нарушает код вставки. Что мне нужно сделать, чтобы оба могли и то, и другое?
const.Разве вы не можете использовать std::tuple здесь? например godbolt.org/z/h3cP83o5d
Кроме того, ваш static_assert() можно уменьшить, поскольку мы знаем, что i == 0 || i == 1 ложно.





Что мне нужно сделать, чтобы оба могли и то, и другое?
- Выполните выбор набора во время компиляции.
- Доступ к наборам осуществляется с помощью изменяемой ссылки и неизменяемой ссылки
const.
В c++23 вывод этого является идеальным решением для таких сценариев.
struct TwoSets
{
template <int i, typename Self>
constexpr auto&& get(this Self&& self ) noexcept
{
if constexpr (i == 0) {
return std::forward<Self>(self).first;
}
else if constexpr (i == 1) {
return std::forward<Self>(self).second;
}
else {
static_assert(i == 0 || i == 1, "invalid");
}
}
std::set<int> first{};
std::set<int> second{};
};
Однако до [tag:C++:23] вам необходимо
const, так и не const члены-геттеры,friend или во внутреннюю функцию.перемещение общей логики в функцию будет выглядеть так:
// Forward declaration
struct TwoSets;
// some internal namespace
template<int i, typename T> constexpr auto& getSet(T&&) noexcept;
struct TwoSets
{
template <int i>
constexpr std::set<int>& get() noexcept {
return getSet<i>(*this);
}
template <int i>
constexpr const std::set<int>& get() const noexcept {
return getSet<i>(*this);
}
std::set<int> first;
std::set<int> second;
};
// some internal namespace
// Friend function to get the set from TwoSets
template<int i, typename T>
constexpr auto& getSet(T&& sets) noexcept
{
// ... code
}
Примечание: если ваш реальный сценарий похож на показанный, вам следует рассмотреть std::tuple, как предложил @paddy в комментарии, и Keep It Simple,& Stupid!"
Функции-члены могут быть перегружены в спецификаторе const. иметь константную и неконстантную перегрузку
get