2015-03-21 3 views
6

У меня есть следующий код.вывести тип элементов кортежа в 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)); 

, который компилятор жалуется на

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)); 

почему компилятор выводить тип первого элемента в кортеже, чтобы быть константный символ (& &) [5]?

+0

Вам даже не нужно фиксировать кортеж. Почему не просто 'template void print_tuple (Tuple && value)'? – 0x499602D2

+0

Поскольку было не ясно, по крайней мере, два человека: ваше определение 'print_tuple' * позволяет * разрешить вызов, если аргументы шаблона явно указаны как' print_tuple '. Это хороший вопрос, почему компилятор выводит первые «Типы» по-разному. – hvd

+0

@hvd Это может быть потому, что строковый литерал является lvalue и отправляется как таковой. – 0x499602D2

ответ

2

Вообще говоря, для успешного дедукции аргумент должен иметь тот же общий вид, что и параметр. Есть некоторые исключения, из которых T && может быть выведено из U & (путем выбора T = U &), но такое исключение не было указано для этого случая.

14.8.2.5 Выведение аргументы шаблона из типа [temp.deduct.type]

8 Тип аргумент шаблона T, шаблон аргумент шаблона TT или шаблон, не аргумент типа i может быть выводится, если P и A иметь один из следующих форм:

[...]

T&
T&&

[...]

Это не совсем ясно, но это требует P (параметр) и A (аргумент), чтобы оба имеют ту же форму. Им необходимо, чтобы оба были из формы T& или обеих форм T&&.Исключения, обстоятельство которых T && может быть выведен из U &, сделано путем изменения T && в простой T до совпадения происходит в ограниченных обстоятельствах:

10 Точно так же, если P имеет форму, которая содержит (T), то каждый тип параметра Pi соответствующего параметра -типа-списка из P сравнивается с соответствующим типом параметра Ai соответствующего параметра -типа-списка из A. Если P и A являются типами функций, которые возникли в результате вычитания при принятии адреса шаблона функции (14.8.2.2) или при выводе аргументов шаблона из объявления функции (14.8.2.6) и Pi и ии Ai являются параметрами верхнего уровня параметра тип-лист из P и A, соответственно, Pi регулируется, если он является ссылкой на Rvalue к CV-неквалифицированным параметру шаблона и Ai является именующей ссылкой, в этом случае типа Pi изменяется на шаблоне тип параметра (т. е. T&& изменяется на T). [...]

и

14.8.2.1 Выведение аргументы шаблона из вызова функции [temp.deduct.call]

3 [...] Если P является rvalue ссылается на cv-неквалифицированный параметр шаблона, и аргумент является lvalue, вместо A используется тип «lvalue reference to A» для вывода типа. [...]

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

Это тот же самый принцип, что делает

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

недействителен: const T не может быть выведено из void(), хотя T = void() даст именно тот результат, и вызов f<void()> удастся.

удален ответ Wintermute показал, что вы можете использовать

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

вместо: это позволяет Types выводиться в виде Lvalue ссылок, как ссылки RValue, или как не-ссылок, в зависимости от типа value.

0

Вы намеревались использовать && в std::tuple<Types&&...> как универсальную ссылку? Это не универсальная ссылка; это ссылка rvalue и может привязываться только к значениям r. Вы можете сделать так, чтобы проверить, какие ссылки есть:

template<typename T> 
class TD; 

Затем определите функцию шаблона:

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; 
} 

Тогда вы увидите ошибку компиляции, как:

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

Вы можете видеть, что даже для типа int он выводит ссылку rvalue. Ссылки Rvalue не могут связываться с lvalues. Вы можете попробовать, что по телефону:

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

Поэтому правильно, что const char(&&)[5] выводится, а строковый литерал не может быть преобразован в const char(&&)[5]. Если вы звоните print_tuple как:

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

Он будет работать. Теперь тип tuple<string&&, int&&>.

+0

Как я прокомментировал вопрос, если явно указывать аргументы шаблона, как в 'print_tuple ', он работает. Этот ответ неверен. 'T &&' не обязательно является ссылкой rvalue. Если 'T' является ссылкой lvalue, тогда' T && 'является просто' T'. Единственная проблема заключается в том, что дедукция не работает: когда 'T &&' предполагается 'U &', 'T' не выводится как' U & ', хотя это может быть. – hvd

+0

@hvd На этом языке нет ничего, что говорит о том, что 'T' может быть выведен как' U & 'в этом контексте. Условия для [temp.deduct.call]/3 не выполняются. Более простым примером является 'template void f (const T &&)', который не будет связываться с lvalues. Если, конечно, вы явно не специализируетесь на шаблоне, что на самом деле не актуально в вопросе о выводе аргумента шаблона. – Oktalist

+0

@Oktalist Это не требует специализирования шаблона. Это просто требует указания аргументов шаблона. Вы можете вызвать 'f ' с выражением 'int' lvalue без каких-либо проблем, хотя вычет никогда не даст вам' T = int & '. Я только что опубликовал ответ с некоторыми подробностями. – hvd

Смежные вопросы