2015-03-21 14 views
6

Mam następujący kod.wyprowadzić typ elementów tuple w C++ 11

template <typename... Types> 
void print_tuple(const std::tuple<Types&&...>& value) 
{ 
    std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; 
} 
print_tuple(std::forward_as_tuple("test",1)); 

które kompilator skarży

error: invalid initialization of reference of type ‘const std::tuple<const char (&&)[5], int&&>&’ from expression of type ‘std::tuple<const char (&)[5], int&&>’ 
    print_tuple(std::forward_as_tuple("test",1)); 

dlaczego kompilator wywnioskować typ pierwszego elementu w krotce za char (& &) [5]?

+0

Nie musisz nawet specjalnie przechwytywać krotki. Dlaczego nie po prostu 'template void print_tuple (Tuple && value)'? – 0x499602D2

+0

Ponieważ nie było to jasne dla co najmniej dwóch osób: twoja definicja 'print_tuple' * nie zezwala na wywołanie, jeśli argumenty szablonu są jawnie określone jako' print_tuple '. To dobre pytanie, dlaczego kompilator inaczej wyprowadza pierwsze "typy". – hvd

+0

@hvd Może to być spowodowane tym, że literał łańcuchowy jest lwartością i jest przekazywany jako taki. – 0x499602D2

Odpowiedz

2

Ogólnie mówiąc, aby odliczenie powiodło się, argument musi mieć tę samą ogólną postać co parametr. Istnieją pewne wyjątki, w których T && można wywnioskować z U & (wybierając T = U &), ale nie podano takiego wyjątku dla tego przypadku.

14.8.2.5 Wydedukowania argumenty szablon z rodzaju [temp.deduct.type]

8 typ szablonu argumentu T, szablon szablon argumentu TT lub szablon bez typu argumentu i może być wywnioskować, czy P i A mieć jedną z następujących form:

[...]

T&
T&&

[...]

To nie jest do końca jasne, ale to wymaga P (parametr) i A (argument) i oba mają taką samą formę. Muszą to być zarówno formularze T&, jak i oba z formularzy T&&.Wyjątki od okoliczności, w których T && można wyprowadzić z U &, są wykonywane przez zmiany T && do zwykłego T przed dopasowanie zachodzi w ograniczonym zakresie:

10 Podobnie, P ma postać, która zawiera (T), a następnie każdy rodzaj parametru Pi odpowiedniego -parametrów typu lista z P jest porównywany z odpowiednim typu parametru Ai odpowiedniego -parametrów typu lista z A. Jeśli P i A są typami funkcji wywodzącymi się z dedukcji podczas przyjmowania adresu szablonu funkcji (14.8.2.2) lub podczas wnioskowania argumentów szablonu z deklaracji funkcji (14.8.2.6) i Pi i Ai są parametrami najwyższego poziomu parametr typu lista- z P i A odpowiednio Pi nastawia jeśli jest to odniesienie RValue do parametru szablonu CV bez zastrzeżeń i Ai jest lwartością odniesienia, w którym to przypadku rodzaj Pi zmienia się być szablon typ parametru (tj. T&& został zmieniony na po prostu T). [...]

i

14.8.2.1 Wydedukowania argumenty szablonów z wywołania funkcji [temp.deduct.call]

3 [...] Jeśli jest P wartość rvalue dla parametru szablonu bez cv, a argumentem jest l-value, zamiast "A" używany jest typ "L-value reference to A". [...]

, ale nie ma podobnego wyjątku dla twojego scenariusza.

Jest to ta sama zasada, która sprawia

template <typename T> struct S { }; 
template <typename T> void f(S<const T>) { } 
int main() { f(S<void()>()); } 

nieprawidłowy: const T nie można wywnioskować z void(), choć T = void() dałby dokładnie ten wynik, a nazywając f<void()> uda.

Wintermute za usunięte odpowiedź pokazał, że można użyć

template <typename... Types>  // vv-- change here 
void print_tuple(const std::tuple<Types...>& value) 

zamiast: pozwala Types zostać wyprowadzona jako odniesienia lwartości, jako odniesienia rvalue lub jako nie-referencji, w zależności od rodzaju value.

0

Czy zamierzano użyć && w std::tuple<Types&&...> jako uniwersalnego odniesienia? To nie jest uniwersalne odniesienie; jest to wartość rvalue reference i może tylko wiązać się z wartościami r. Można to zrobić w ten sposób, by sprawdzić, jaki rodzaj odniesienia jest:

template<typename T> 
class TD; 

Następnie zdefiniować funkcję szablonu:

template <typename... Types> 
void print_tuple(const std::tuple<Types&&...>& value) 
{ 
    TD<decltype(value)> a; 
    std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; 
} 

Następnie pojawi się błąd kompilacji jak:

implicit instantiation of undefined template 'TD<const std::__1::tuple<std::__1::basic_string<char> &&, int &&> &>' 

Widać, że nawet dla typu int, zakłada on, że jest to rvalue reference. Odniesienia do wartości R nie mogą wiązać się z wartościami l. Można spróbować, pod numerem:

int i = 1; 
print_tuple(std::forward_as_tuple(i,1)); // error. 

Dlatego prawdą jest, że const char(&&)[5] jest wydedukować, a ciąg dosłowny nie mogą być konwertowane do const char(&&)[5]. Jeśli zadzwonisz pod numer: print_tuple:

print_tuple(std::forward_as_tuple(string("test"),1)); 

To zadziała. Teraz typem jest tuple<string&&, int&&>.

+0

Tak jak skomentowałem to pytanie, jeśli jawnie wypisuję argumenty szablonu, jak w 'print_tuple ', to działa. Ta odpowiedź jest błędna. 'T &&' niekoniecznie jest referencją rwartości. Jeśli 'T' jest wartością odniesienia l, to' T && 'jest po prostu' T'. Jedynym problemem jest to, że dedukcja nie działa: kiedy 'T &&' ma być 'U &', 'T' nie jest wydedukowane jako' U & ', nawet jeśli mogłoby być. – hvd

+0

@hvd Nie ma nic w języku, który mówi, że "T" można wywnioskować jako "U &" w tym kontekście. Warunki dla [temp.dokład.]/3 nie są spełnione. Prostszym przykładem jest 'template void f (const T &&)' który nie będzie wiązał się z lwartościami. O ile oczywiście wyraźnie nie wyspecyfikujesz szablonu, co nie jest istotne w pytaniu o odrzucenie argumentu szablonu. – Oktalist

+0

@Oktalist Nie wymaga specjalizacji szablonu. Wymaga jedynie podania argumentów szablonu. Możesz wywołać 'f ' z wyrażeniem 'int' l bez żadnych problemów, nawet jeśli dedukcja nigdy nie da ci' T = int & '. Właśnie wysłałem odpowiedź z kilkoma dodatkowymi szczegółami. – hvd