2016-07-13 17 views
11

Dato il seguente riferimento collasso regolePerché std :: forward ha due sovraccarichi?

  • T& & ->T&
  • T&& & ->T&
  • T& && ->T&
  • T&& && ->T&&

L' terza e quarta regola Ciò implica che T(ref qualifer) && è la trasformazione dell'identità, ovvero T& rimane allo T& e T&& rimane allo T&&. Perché abbiamo due sovraccarichi per std::forward? La seguente definizione non può servire a tutti gli scopi?

template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>> 
T&& forward(const typename std::remove_reference<T>::type& val) { 
    return static_cast<T&&>(const_cast<T&&>(val)); 
} 

Qui l'unico scopo della const std::remove_reference<T>& serve è quello di non fare copie. E lo enable_if garantisce che la funzione venga richiamata solo su valori non const. Non sono completamente sicuro se sia necessario lo const_cast poiché non è il riferimento stesso che è const.

Dal forward è sempre chiamato con parametri di modello espliciti ci sono due casi dobbiamo considerare:

  1. forward<Type&>(val) Ecco il tipo di T in forward sarà T& e quindi il tipo di ritorno sarà la trasformazione identità T&
  2. forward<Type&&>(val) Qui il tipo di T in forward sarà T&& e quindi il tipo di ritorno è la trasformazione di identità T&&

Allora, perché abbiamo bisogno di due sovraccarichi come descritto in http://en.cppreference.com/w/cpp/utility/forward?

Nota: io non sono sicuro se std::forward utilizza sempre con const tipi, ma io disabile forward in quel caso, perché non ho mai visto usato in quel modo. Anche spostare la semantica non ha davvero senso in quel caso.

+0

Becuase rvalue non può essere associato a 'val' se non c'è il 2o sovraccarico? – songyuanyao

+0

@songyuanyao whoops, ho avuto un const in là prima, ma in qualche modo ho deciso di rimuoverlo ... – Curious

+0

L'esempio che dai non può essere associato a rvalue (vedi [questo esempio] (http://coliru.stacked-crooked.com/ a/b0ae1eaf909513e0)) – Rerito

risposta

7

Un buon punto di partenza sarebbe quello di Howard Hinnant answer e paper su std::forward().


L'implementazione gestisce tutte le normali casi d'uso correttamente (T& --> T&, T const& --> T const& e T&& --> T&&). Ciò che non riesce a gestire sono errori comuni e facili da eseguire, errori che sarebbero molto difficili da eseguire nel debug dell'implementazione ma non riescono a compilare con std::forward().

Attribuite queste definizioni:

struct Object { }; 

template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>> 
T&& my_forward(const typename std::remove_reference<T>::type& val) { 
    return static_cast<T&&>(const_cast<T&&>(val)); 
} 

template <class T> 
void foo(T&&) { } 

posso passare non const riferimenti a const oggetti, sia della varietà lvalue:

const Object o{}; 
foo(my_forward<Object&>(o)); // ok?? calls foo<Object&> 
foo(std::forward<Object&>(o)); // error 

e la varietà rvalue:

const Object o{}; 
foo(my_forward<Object>(o)); // ok?? calls foo<Object> 
foo(std::forward<Object>(o)); // error 

Posso passare i riferimenti lvalue ai rvalue:

foo(my_forward<Object&>(Object{})); // ok?? calls foo<Object&> 
foo(std::forward<Object&>(Object{})); // error 

I primi due casi portano a oggetti potenzialmente modificanti che avevano lo scopo di essere const (che potrebbe essere UB se sono stati costruiti const), l'ultimo caso sta passando un riferimento Ivalue penzoloni.

Problemi correlati