The following code fails to compile with the error template template argument has different template parameters than its corresponding template template parameter
:
#include <tuple>
#include <vector>
#include <string>
#include <iostream>
template<template<typename, typename> class Blah, typename KeyType, typename ValueType>
void print_tuploid(const Blah<KeyType, ValueType>& tup) {
std::cout << "first value: " << std::get<0>(tup) << " second value: " << std::get<1>(tup) << std::endl;
}
int main() {
auto stuff = std::make_tuple(1, 2);
print_tuploid(stuff);
}
The original intention behind this code is irrelevant, at this point I'm just trying to understand why this is considered to be invalid.
If I change the call to std::make_tuple
into std::make_pair
, the code compiles and runs correctly, which leads me to believe there's something weird going on that's specific to std::tuple
.
I was originally thinking that std::tuple
might have some extra, defaulted, template arguments I wasn't aware of, because if I change the definition of print_tuploid
to the following, the code DOES compile for both std::make_tuple
and std::make_pair
:
template<template<typename...> class Blah, typename KeyType, typename ValueType>
void print_tuploid(const Blah<KeyType, ValueType>& tup) {
std::cout << "first value: " << std::get<0>(tup) << " second value: " << std::get<1>(tup) << std::endl;
}
But when I tried to dump out the deduced type of stuff
using the following code:
#include <tuple>
template<typename T>
class TypePrinter;
int main() {
auto stuff = std::make_tuple(1, 2);
TypePrinter<decltype(stuff)> error;
}
It reported: implicit instantiation of undefined template 'TypePrinter<std::__1::tuple<int, int> >'
, leading me to believe this is not the case.
Also, as a side question, why does reference collapsing not occur in this context? The following code is also considered invalid:
#include <iostream>
template<template<typename, typename> class Blah, typename KeyType, typename ValueType>
void print_tuploid(Blah<KeyType, ValueType>&& tup) {
std::cout << "first value: " << std::get<0>(tup) << " second value: " << std::get<1>(tup) << std::endl;
}
int main() {
auto stuff = std::make_pair(1, 2);
print_tuploid(stuff);
}
Giving the error: no known conversion from 'std::__1::pair<int, int>' to 'pair<int, int> &&' for 1st argument
.
Basically I'm just trying to extend my template knowledge by understanding what exactly is going on here. Sorry for the long post, and thanks in advance for any guidance anyone is able to provide.
Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement.
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.
The issue with the function is that it matches against a template class that takes exactly 2 arguments. In reality, std::tuple
has the template signature of template <typename...>
. The distinction lies in that tuple
takes an arbitrary amount of template parameters, while your function expects 2.
std::pair
works fine because the template signature is template <typename, typename>
which matches exactly.
There is a proposal, although I am not able to find the latest version, that enables this sort of specialization. It seems to have passed into C++17, or at least something similar to it, as the cppreference page for template template parameters considers this a valid example. The proposal I found is here.
The reason why your second version does not accept std::pair
is because it expects an rvalue reference. Once you have a (partially) specialized type, you cannot treat &&
as a forwarding reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With