Невозможно создать несколько геттеров constexpr

У меня есть структура, которая содержит несколько данных. Я хочу, чтобы доступ к наборам вычислялся во время компиляции. Для этого я создал шаблонный метод получения 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, что нарушает код вставки. Что мне нужно сделать, чтобы оба могли и то, и другое?

  1. Выполните выбор набора во время компиляции
  2. Доступ к наборам с помощью изменяемой ссылки и неизменяемой ссылки const.

Функции-члены могут быть перегружены в спецификаторе const. иметь константную и неконстантную перегрузку get

NathanOliver 09.04.2024 01:43

Разве вы не можете использовать std::tuple здесь? например godbolt.org/z/h3cP83o5d

paddy 09.04.2024 01:48

Кроме того, ваш static_assert() можно уменьшить, поскольку мы знаем, что i == 0 || i == 1 ложно.

Toby Speight 14.04.2024 10:20
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
89
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Что мне нужно сделать, чтобы оба могли и то, и другое?

  1. Выполните выбор набора во время компиляции.
  2. Доступ к наборам осуществляется с помощью изменяемой ссылки и неизменяемой ссылки 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] вам необходимо

  1. либо предоставить как const, так и не const члены-геттеры,
  2. или переместите общую логику в нечлен 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!"

Другие вопросы по теме