Могу ли я избавиться от этого члена класса в этом шаблоне? - PullRequest
2 голосов
/ 17 июня 2020

У меня был шаблонный класс с функцией получения и указателем на функцию установки. Когда я создал объект этого класса и не хотел, чтобы его установщик выполнял какие-либо действия, я просто передал nullptr в качестве аргумента функции установщика. Затем во время выполнения он проверит, был ли его указатель установщика nullptr, чтобы узнать, должен ли он вызывать установщик.

Использование указателя функции сейчас недостаточно, потому что я хочу, чтобы класс мог хранить лямбда. Таким образом, вместо указателя на функцию установщик теперь является аргументом типа шаблона. Раньше я передавал nullptr, чтобы указать, что сеттера не было, но теперь я не могу, поэтому я использую фиктивный член char, как вы можете видеть:

template <typename GetFunctionType, typename SetFunctionType = char>
class MyClass
{
public:
   MyClass(GetFunctionType getFunction, SetFunctionType setFunction = char(0)) 
      : getFunction(getFunction), setFunction(setFunction)
   {}
   GetFunctionType getFunction;
   SetFunctionType setFunction;
   typedef decltype(getFunction()) ReturnType;

   void set(ReturnType value)
   {
      if constexpr (std::is_invocable_v<decltype(setFunction), ReturnType>)
      {
         setFunction(value);
         std::cout << "Setter called\n";
      }
      else
      {
         // ELSE DO NOTHING
         std::cout << "Object has no setter\n";
      }
   }
};

int main()
{
   MyClass foo([]() { return 7; }, [](int val) { std::cout << "You have set the value\n"; });

   MyClass foo2([]() {return 7; }); // THIS OBJECT HAS NO SETTER, BUT HAS A CHAR 
                                    // MEMBER THAT I USED AS A DUMMY
   foo.set(1);
   foo2.set(1);
}

Мой вопрос: нужен ли мне этот фиктивный символ в тех случаях, когда объект не имеет функции установки?

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

Использование указателя функции сейчас недостаточно, потому что я хочу, чтобы класс мог хранить лямбду!

Не совсем верно!

Вы могли бы сохранить лямбды без захвата в указатели типизированных функций.

См. [expr.prim.lambda.closure] (se c 7)

Тип закрытия для лямбда-выражения, отличного от общего c с без лямбда-захвата , чьи ограничения (если есть) удовлетворены, имеет функцию преобразования в указатель на функцию с привязкой к языку C ++, имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции закрывающего типа.

В дополнение к этому, обычно Получатель будет иметь подпись

ReturnType /*const-ref*/ <function>();

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

void <function>(ArgumentType) /*const*/; // where ReturnType  == ArgumentType usually

Объединяя эти две информации, я предлагаю следующую реструктуризацию вашего класса.

( Посмотреть онлайн-демонстрацию )

#include <iostream>
#include <string>

template <typename ArgType>
class MyClass final
{
   // alias types
   using GetFunctionType = ArgType(*)(void);
   using SetFunctionType = void(*)(ArgType);

   GetFunctionType getFunction;
   SetFunctionType setFunction;
public:
   // Now you can set the function pointer to by default `nullptr`
   MyClass(GetFunctionType getFunction = nullptr, SetFunctionType setFunction = nullptr)
      : getFunction{getFunction}
      , setFunction{setFunction}
   {}

   void set(ArgType value) const noexcept
   {
      if (getFunction && setFunction)  // runtime nullptr check
      {
         setFunction(value);
         std::cout << "Setter called\n\n\n";
      } else {
         std::cout << "Object has no setter\n\n";
      }
   }
};

int main()
{
   // mention the argument type
   MyClass<int> foo(
      []() { return 7; },
      [](int val) { std::cout << "You have set the value: " << val << "\n"; }
   );

   MyClass<std::string> foo2([]() {return std::string{}; }); // also works

   foo.set(1);
   foo2.set("String");
}
1 голос
/ 20 июня 2020

Было бы хорошо, если бы был способ передать void или 'something', чтобы при объявлении члена класса следующим образом: T setFunction; компилятор просто удаляет его из класса.

Насколько я понимаю, когда вы выполняете частичную специализацию , вам даже не нужно объявлять setFunction вообще.

Ниже приведен пример кода, в котором первая специализация

template <typename Getter, typename Setter>
class MyClass final{};

обрабатывает случаи, когда вы предоставляете Getter и Setter, а вторая - ситуация без сеттера .

template <typename Getter>
class MyClass<Getter, std::nullptr_t> final

К сожалению, вам все равно нужно указать второй аргумент (т.е. std::nullptr_t{}), чтобы выбрать правильную специализацию.

( Посмотреть живую демонстрацию онлайн )

#include <iostream>
#include <cstddef>  // std::nullptr_t

template <typename Getter, typename Setter>
class MyClass final
{
   Getter mGetFunction;
   Setter mSetFunction;
   using ReType = decltype(mGetFunction());
   static_assert(std::is_invocable_v<decltype(mGetFunction)>, " Getter is not callable!");
   static_assert(std::is_invocable_v<decltype(mSetFunction), ReType>, " Setter is not callable!");

public:
   MyClass(Getter mGetFunction, Setter mSetFunction) noexcept
      : mGetFunction{ mGetFunction }
      , mSetFunction{ mSetFunction }
   {}
   void set(ReType value) const noexcept {
      mSetFunction(value);
      std::cout << "Setter called\n";
   }

};

template <typename Getter>
class MyClass<Getter, std::nullptr_t> final
{
   Getter mGetFunction;
   using ReType = decltype(mGetFunction());
   static_assert(std::is_invocable_v<decltype(mGetFunction)>, " Getter is not callable!");

public:
   MyClass(Getter mGetFunction, std::nullptr_t) noexcept
      : mGetFunction{ mGetFunction }
   {}

   void set(ReType value) const noexcept {
      std::cout << "Object has no setter\n";
   }
};

int main()
{
   MyClass foo{
      []() { return 7; },
      [](int val) { std::cout << "You have set the value\n"; }
   };
   foo.set(1);

   //unfortunately, needed to pass second argument for class instantiation
   MyClass foo2([]() {return 7; }, std::nullptr_t{});
   foo2.set(1);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...