Прекрасно переадресация функции оболочки?

Рассмотрим следующий функтор, чтобы помочь определить, какая версия его operator() называется.

struct functor
{
    void operator()() {std::cout << "functor::operator" << std::endl;}
    void operator()() const {std::cout << "functor::operator const" << std::endl;}
    void operator()() volatile {std::cout << "functor::operator volatile" << std::endl;}
    void operator()() const volatile {std::cout << "functor::operator const volatile" << std::endl;}
    void operator()(int) & {std::cout << "functor::operator &" << std::endl;}
    void operator()(int) const& {std::cout << "functor::operator const&" << std::endl;}
    void operator()(int) volatile& {std::cout << "functor::operator volatile&" << std::endl;}
    void operator()(int) const volatile& {std::cout << "functor::operator const volatile&" << std::endl;}
    void operator()(int) && {std::cout << "functor::operator &&" << std::endl;}
    void operator()(int) const&& {std::cout << "functor::operator const&&" << std::endl;}
    void operator()(int) volatile&& {std::cout << "functor::operator volatile&&" << std::endl;}
    void operator()(int) const volatile&& {std::cout << "const volatile&&" << std::endl;}
};

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

  • Первый wrapper1 который будет вызывать оператор в зависимости от квалификатора функтора
  • Второй wrapper2 который будет вызывать оператор в зависимости от квалификатора оболочки

Например:

wrapper1 w1(functor{});
wrapper2 w2(functor{});
w1(0); // should call the && version since the functor was built as a temporary
w2(0); // should call the & version since the wrapper was built as a value

Вот некоторые очень грубые исследования того, что я ищу. Примечание: это просто, чтобы дать "вкус" того, о чем я думаю.

template <class F>
struct wrapper_a
{
    constexpr wrapper_a(F f) noexcept: _tuple(f) {}
    template <class... Args>
    void operator()(Args&&... args)
    {std::get<0>(_tuple)(std::forward<Args>(args)...);}
    std::tuple<F> _tuple;
};

template <class F>
struct wrapper_b
{
    template <class G>
    constexpr wrapper_b(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
    template <class... Args>
    void operator()(Args&&... args)
    {std::get<0>(_tuple)(std::forward<Args>(args)...);}
    std::tuple<F> _tuple;
};

template <class F> wrapper_b(F&& f) -> wrapper_b<F>;

template <class F>
struct wrapper_c
{
    template <class G>
    constexpr wrapper_c(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
    template <class... Args>
    void operator()(Args&&... args)
    {std::forward<F>(std::get<0>(_tuple))(std::forward<Args>(args)...);}
    std::tuple<F> _tuple;
};

template <class F> wrapper_c(F&& f) -> wrapper_c<F>;

Как добиться того, что я ищу? Это вообще возможно?

1 ответ

Решение

Не уверен, что точно понимаю ваши требования... и я новичок с переадресацией ссылок... в любом случае

Первый wrapper1, который будет вызывать оператор в зависимости от квалификатора функтора.

Может быть, эксперт может избежать этого осложнения, но мне кажется, что вам нужны два параметра шаблона

template <typename F, typename G>
struct wrapper1

где F тип копии параметра, переданного в конструктор и G это предполагаемый тип.

Так что у вас есть в этом F значение

F f;

и вы можете использовать его через пересылку

template <typename ... Args>
void operator() (Args && ... as) &
 { std::forward<G>(f)(std::forward<Args>(as)...); }

Чтобы упростить, вы можете определить шаблон вычета следующим образом

template <typename F>
wrapper1(F && f) -> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;

Второй wrapper2, который будет вызывать оператор в зависимости от квалификатора оболочки

Может быть, я ошибаюсь, но это кажется мне немного проще.

Вам нужен только параметр шаблона

template <typename F>
struct wrapper2

и вам нужно только использовать std::move() в r-эталонных операторах

template <typename ... Args>
void operator() (Args && ... as) &&
 { std::move(f)(std::forward<Args>(as)...);  }

Руководство по выводу просто

template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;

Следующее является ограниченным (для const/не-const и эталонные альтернативы l-значения / r-значения), но полные рабочие примеры.

Обратите внимание, что есть некоторые ошибки компиляции, вызванные проблемой константности (если вы инициализируете const wrapper1 с не-const вызываемый, есть проблемы)

#include <iostream>

struct functor
 {
   void operator()(int) & 
    {std::cout << "functor::operator &" << std::endl;}
   void operator()(int) const &
    {std::cout << "functor::operator const &" << std::endl;}
   void operator()(int) &&
    {std::cout << "functor::operator &&" << std::endl;}
   void operator()(int) const &&
    {std::cout << "functor::operator const &&" << std::endl;}
 };

template <typename F, typename G>
struct wrapper1
 {
   template <typename H>
   constexpr wrapper1 (H && f0) noexcept : f{std::forward<H>(f0)}
    {}

   template <typename ... Args>
   void operator() (Args && ... as) &
    { std::forward<G>(f)(std::forward<Args>(as)...); }

   template <typename ... Args>
   void operator() (Args && ... as) const &
    { std::forward<G>(f)(std::forward<Args>(as)...); }

   template <typename ... Args>
   void operator() (Args && ... as) &&
    { std::forward<G>(f)(std::forward<Args>(as)...);  }

   template <typename ... Args>
   void operator() (Args && ... as) const &&
    { std::forward<G>(f)(std::forward<Args>(as)...);  }

   F f;
 };

template <typename F>
wrapper1(F && f) 
   -> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;

template <typename F>
struct wrapper2
 {
   template <typename H>
   constexpr wrapper2 (H && f0) noexcept : f{std::forward<H>(f0)}
    {}

   template <typename ... Args>
   void operator() (Args && ... as) &
    { f(std::forward<Args>(as)...); }

   template <typename ... Args>
   void operator() (Args && ... as) const &
    { f(std::forward<Args>(as)...); }

   template <typename ... Args>
   void operator() (Args && ... as) &&
    { std::move(f)(std::forward<Args>(as)...);  }

   template <typename ... Args>
   void operator() (Args && ... as) const &&
    { std::move(f)(std::forward<Args>(as)...);  }

   F f;
 };

template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;

int main ()
 {
   functor       fc;
   functor const fd;

   wrapper1 w1a{functor{}};
   wrapper1 w1b{static_cast<functor const &&>(functor{})};
   wrapper1 w1c{fc};
   wrapper1 w1d{fd};

   wrapper1 const w1e{functor{}};
   wrapper1 const w1f{static_cast<functor const &&>(functor{})};
   wrapper1 const w1g{fc};
   wrapper1 const w1h{fd};

   w1a(0);
   w1b(0);
   w1c(0);
   w1d(0);

   std::cout << "----------------------------" << std::endl;

   // w1e(0); // compilation error
   w1f(0);
   // w1g(0); // compilation error
   w1h(0);

   std::cout << "----------------------------" << std::endl;

   wrapper1<functor, functor&&>{functor{}}(0);
   wrapper1<functor, functor const &&>
      {static_cast<functor const &&>(functor{})}(0);
   wrapper1<functor, functor&>{fc}(0);
   wrapper1<functor, functor const &>{fd}(0);

   std::cout << "----------------------------" << std::endl;

   // (wrapper1 <functor, functor&&> const)
      //{functor{}}(0); // compilation error
   (wrapper1<functor, functor const &&> const)
      {static_cast<functor const &&>(functor{})}(0);
   // (wrapper1<functor, functor&> const){fc}(0); // compilation error
   (wrapper1<functor, functor const &> const){fd}(0);

   wrapper2 w2a{functor{}};
   wrapper2 w2b{static_cast<functor const &&>(functor{})};
   wrapper2 w2c{fc};
   wrapper2 w2d{fd};

   wrapper2 const w2e{functor{}};
   wrapper2 const w2f{static_cast<functor const &&>(functor{})};
   wrapper2 const w2g{fc};
   wrapper2 const w2h{fd};

   std::cout << "----------------------------" << std::endl;

   w2a(0);
   w2b(0);
   w2c(0);
   w2d(0);

   std::cout << "----------------------------" << std::endl;

   w2e(0);
   w2f(0);
   w2g(0);
   w2h(0);

   std::cout << "----------------------------" << std::endl;

   wrapper2<functor>{functor{}}(0);
   wrapper2<functor>{static_cast<functor const &&>(functor{})}(0);
   wrapper2<functor>{fc}(0);
   wrapper2<functor>{fd}(0);

   std::cout << "----------------------------" << std::endl;

   (wrapper2<functor> const){functor{}}(0);
   (wrapper2<functor> const){static_cast<functor const &&>(functor{})}(0);
   (wrapper2<functor> const){fc}(0);
   (wrapper2<functor> const){fd}(0);

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