Questo è un male:
auto q = std::tuple<A&&,A&&>(A{100},A{200});
si sta costruendo un tuple
di riferimenti rvalue per provvisori che vengono distrutti alla fine dell'espressione, così si è lasciato con riferimenti penzoloni.
L'istruzione corretta sarebbe:
std::tuple<A, A> q(100, 200);
Tuttavia, fino a poco tempo fa, quanto sopra è stato non supportato dallo standard. Nel N4296, la formulazione intorno il costruttore rilevante per tuple
è [tuple.cnstr]:
Richiede: sizeof...(Types) == sizeof...(UTypes)
. is_constructible<Ti, Ui&&>::value
è true per tutti i
.
Effetti: Inizializza gli elementi nella tupla con il valore corrispondente in std::forward<UTypes>(u)
.
Nota: Questo costruttore non deve partecipare a risoluzione di sovraccarico a meno che ogni tipo in UTypes
è implicitamente convertibile al suo tipo corrispondente Types
.
Quindi, questo costruttore non partecipava alla risoluzione di sovraccarico a causa int
non è implicitamente convertibile in A
. Questo è stato risolto con l'adozione di Improving pair
and tuple
, che ha affrontato proprio il vostro caso d'uso:
struct D { D(int); D(const D&) = delete; };
std::tuple<D> td(12); // Error
La nuova formulazione di questo costruttore è, da N4527:
Note: Questo costruttore non deve partecipare alla risoluzione di sovraccarico a meno che sizeof...(Types) >= 1
e is_constructible<Ti, Ui&&>::value
sia vero per tutto i
.Il costruttore è esplicito se e solo se is_convertible<Ui&&, Ti>::value
è false
per almeno uno i.
E is_constructible<A, int&&>::value
è vero.
Per presentare la differenza in un altro modo, ecco un'implementazione tupla estremamente ridotta:
struct D { D(int) {} D(const D&) = delete; };
template <typename T>
struct Tuple {
Tuple(const T& t)
: T(t)
{ }
template <typename U,
#ifdef USE_OLD_RULES
typename = std::enable_if_t<std::is_convertible<U, T>::value>
#else
typename = std::enable_if_t<std::is_constructible<T, U&&>::value>
#endif
>
Tuple(U&& u)
: t(std::forward<U>(u))
{ }
T t;
};
int main()
{
Tuple<D> t(12);
}
Se USE_OLD_RULES
è definito, il primo costruttore è l'unico costruttore efficiente e quindi il codice non compila dal D
è noncopyable. Altrimenti, il secondo costruttore è il miglior candidato possibile e quello è ben formato.
L'adozione era abbastanza recente che né gcc 5.2, né clang 3.6 in realtà si compilare questo esempio ancora. Quindi avrai bisogno di un compilatore più recente di quello (gcc 6.0 funziona) o di un design diverso.
'std :: tuple (A {100}, {A 200});' è una tupla di riferimenti ciondolanti. Penso 'std :: tuple p (100, 200);' dovrebbe funzionare invece –
@PiotrSkotnicki Non si compila su clang o g ++: http://coliru.stacked-crooked.com/a/1fe2146b7881e241 – NathanOliver
@NathanOliver it fa, o usa 'libC++' o un più recente 'libstdC++' –