3

Рассмотрим следующий код, который создает функции хранения класса.Совершенная пересылка функций для построения класса списка функций

// Function list class 
template <class... F> 
struct function_list 
{ 
    template <class... G> 
    constexpr function_list(G&&... g) noexcept 
    : _f{std::forward<G>(g)...} 
    { 
    } 
    std::tuple</* F... OR F&&... */> _f; 
}; 

// Function list maker 
template <class... F, class R = /* Can we compute the return type here? */> 
constexpr R make_function_list(F&&... f) 
{ 
    return function_list< 
     /* decltype(std::forward<F>(f))... 
     * OR F... 
     * OR F&&... 
     */>(std::forward<F>(f)...); 
} 

Я хотел бы эти функции должны быть идеально переадресованы (независимо от того, являются ли они указатели на функции, функторы, лямбда ...). Но я не совсем понимаю, какой тип дедукции происходит за std::forward и универсальными ссылками. В приведенном выше коде, у меня есть три вопроса:

  • Должен _f быть типа std::tuple<F...> или std::tuple<F&&...>
  • Можно ли вывести тип R возврата в списке параметров шаблона (потому что это делать (и почему?) вручную вместо auto/decltype(auto) было бы полезно, чтобы понять, что происходит)
  • в мейкера, что function_list аргумент шаблона должен быть: decltype(std::forward<F>(f)...), F или F&&... (и почему)

Примечание: конструктор function_list не должен вызываться напрямую, вместо этого make_function_list выполняет свою работу.

EDIT: В этом случае сейф, если operator() из function_list (не показан) не может быть вызван в том же заявлении?

template <class... F> 
constexpr function_list<F...> make_function_list(F&&... f) 
{ 
    return function_list<F&&...>(std::forward<F>(f)...); 
} 
+0

Вы почти наверняка ** не ** хочу им быть 'F &&'. – SergeyA

+0

1) 'tuple ' 2) Вам не понадобится 'R' в списке шаблонов, вы уже знаете, что тип возвращаемого значения -' function_list '3)' F && ', потому что это ссылка пересылки, которая связывает оба с rvalue и lvalue refs (вы сказали, что хотите совершенную пересылку, так вот как вы ее получите) – AndyG

+1

, если вы можете гарантировать, что оператор вызова функции вызывается в том же самом выражении, что и 'make_function_list', вы можете использовать' F && '(вы можете выражаем, что с использованием ref refifier '&&', на 'operator()') –

ответ

3

Но я не совсем понимаю, какой тип вычитания происходит за std::forward и универсальными ссылками.

Это довольно просто понять на примере.

template <typename T> 
void f(T&&) 
{ 
    std::tuple<T>{}; // (0) 
    std::tuple<T&&>{}; // (1) 
} 

В случае (0):

  • T выводится в T для rvalues ​​
  • T выводится в T& для lvalues ​​.

В случае (1):

  • T выводится в T&& для rvalues ​​
  • T выводится в T& для lvalues ​​.

Как вы можете видеть, единственное различие между ними состоит в том, как rvalues ​​ выводятся.

Что касается std::forward, это то, что он делает:

template <typename T> 
void g(T&&); 

template <typename T> 
void f(T&& x) 
{ 
    g(x) // (0) 
    g(std::forward<T>(x)); // (1) 
} 

В случае (0):

  • x всегда является именующего.

В случае (1):

  • x отливают в T&&, если T выводится в T.

  • x остается lvalue в противном случае.

std::forward в основном сохраняет категорию типа в x, посмотрев на то, как T выводилось.


Если _f быть типа std::tuple<F...> или std::tuple<F&&...>

Я думаю, что в вашем случае это должно быть std::tuple<F...>, как вы хотите сохранить либо Lvalue ссылки или значения.

std::tuple<F&&...> будет хранить либо Lvalue ссылки или Rvalue References - которые приведут к оборванных ссылок в случае временных.


Можно ли вывести тип возвращаемого R в списке параметров шаблона

Да, это просто function_list<F...>.

template <class... F, class R = function_list<F...>> 
constexpr R make_function_list(F&&... f) 
{ 
    return function_list<F...>(std::forward<F>(f)...); 
} 

Вам не нужно даже параметр R шаблона.

template <class... F> 
constexpr function_list<F...> make_function_list(F&&... f) 
{ 
    return function_list<F...>(std::forward<F>(f)...); 
} 

В мейкера, что function_list аргумент шаблона должен быть: decltype(std::forward<F>(f)...), F или F&&...

function_list следует принимать F... в качестве параметра шаблона по причинам, перечисленным на начало этого ответа (т. е. избегая оборванных ссылок на временные рамки).

Это все равно должны std::forward<F>(f)... в качестве аргументов, чтобы rvalues ​​ быть перенаправлены в таких (т.е. движущихся rvalues ​​в function_list «s кортежа).

2

Если они F&&, а затем, если вы передаете временный к make_function_list, возвращаемый класс, состоящий из tuple будет хранить ссылку RValue на временный передается make_function_list.

На следующей строке это теперь болтающаяся ссылка.

Это кажется плохим в большинстве случаев использования. Это на самом деле не плохо в все прецеденты; forward_as_tuple делает. Но такие варианты использования: не общий предел. Картина чрезвычайно хрупкая и опасная.

В целом, если вы возвращаете T&&, вы хотите вернуть его как T. Это может привести к копированию объекта; но альтернативой является danging-reference-hell.

Это дает нам:

template<class... Fs> 
struct function_list { 
    template<class... Gs> 
    explicit constexpr function_list(Gs&&... gs) noexcept 
    : fs(std::forward<Gs>(gs)...) 
    {} 
    std::tuple<Fs...> fs; 
}; 
template<class... Fs, class R = function_list<Fs...>> 
constexpr R make_function_list(Fs&&... fs) { 
    return R(std::forward<Fs>(fs)...); 
} 

Также сделайте function_list «S explicit т е р, так как в случае 1 аргумента он переходит к довольно жадный конструктор преобразования неявной. Это может быть исправлено, но требует больше усилий, чем того стоит.

operator() требует экземпляра. Имя типа не является экземпляром.

0

Это зависит от того, что function_list для. Там в основном два случая:

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

    template <class... F> 
    struct function_list 
    { 
        std::tuple<F&&...> f_; 
    
        // no need to make this template 
        constexpr function_list(F&&... f) noexcept 
         : f_{std::forward<F>(f)...} 
        {} 
    
        template <std::size_t i, typename... A> 
        decltype(auto) call_at(A&&... a) 
        { 
         return std::invoke(std::get<i>(f_), std::forward<A>(a)...); 
        } 
    }; 
    
  2. function_list обертка/объект контейнера сродни std::bind, в этом случае вы хотите сохранить сгнившие копии функций, чтобы избежать оборванных ссылок и совершенную переадресацию в этом контексте будет означать экспедиционные функции к застройщикам их разложившиеся версии в f_, а затем в точке вызова наполняя сгнившие функции со значением категории самого function_list:

    template <class... F> 
    struct function_list 
    { 
        std::tuple<std::decay_t<F>...> f_; 
    
        template <typename... G> 
        constexpr function_list(G&&... g) 
         : f_{std::forward<G>(g)...} 
        {} 
    
        template <std::size_t i, typename... A> 
        decltype(auto) call_at(A&&... a) & 
        { 
         return std::invoke(std::get<i>(f_), std::forward<A>(a)...); 
        } 
    
        template <std::size_t i, typename... A> 
        decltype(auto) call_at(A&&... a) const& 
        { 
         return std::invoke(std::get<i>(f_), std::forward<A>(a)...); 
        } 
    
        template <std::size_t i, typename... A> 
        decltype(auto) call_at(A&&... a) && 
        { 
         return std::invoke(std::get<i>(std::move(f_)), std::forward<A>(a)...); 
        } 
    
        template <std::size_t i, typename... A> 
        decltype(auto) call_at(A&&... a) const&& 
        { 
         return std::invoke(std::get<i>(std::move(f_)), std::forward<A>(a)...); 
        } 
    }; 
    

    Как std::bind, если вы на самом деле хотите сохранить ссылку, вы должны сделать это явно с std::reference_wrapper.

Строительство одинакова в обоих случаях:

template <class... F> 
constexpr auto make_function_list(F&&... f) 
{ 
    return function_list<F...>(std::forward<F>(f)...); 
} 
Смежные вопросы