Question:
For example, there is this template:
template <typename ValueType, template <typename> typename Container>
void foo(const Container<ValueType> &container) {
std::cout << "hello" << std::endl;
}
in which one of the template parameters is itself a template. As far as I know, this is a perfectly valid construct . Now let's do a small example:
// main.cpp
#include <cstdlib>
#include <vector>
template <typename ValueType, template <typename> typename Container>
void foo(const Container<ValueType> &container) {
}
int main() {
std::vector<int> tmp;
foo(tmp);
return EXIT_SUCCESS;
}
This example compiles without problems with gcc-8.3.0
, but does not compile with clang-9.0.1
, which throws the following error:
candidate template ignored: substitution failure [with ValueType = int]:
template template argument has different template parameters than its
corresponding template template parameter
void foo(const Container<ValueType> &container) {
^
Why is this happening? Is this a bug?
Answer:
A vector has not one template parameter, but two: https://en.cppreference.com/w/cpp/container/vector
In general, the behavior of CLang corresponds to the language standard, since there is a discrepancy in the number of template parameters. Hence the error.
This is how it will work:
#include <vector>
template <
typename ValueType,
template <typename, typename> class Container
> void foo(
const Container<ValueType, std::allocator<ValueType>>& container
)
{}
int main() {
std::vector<int> tmp;
foo(tmp);
return 0;
}
It is worth noting that STL containers generally have a different number of parameters. In this case, the following code makes sense:
#include <vector>
#include <list>
#include <set>
template <
typename... Params,
template <typename...> class Container
> void foo(
const Container<Params...>& container
)
{}
int main() {
std::vector<int> vec; // 2 шаблонных параметра
std::list<double> lst; // 2 шаблонных параметра
std::set<int> set; // 3 шаблонных параметра
foo(vec);
foo(lst);
foo(set);
return 0;
}
UPD: In the comments I wrote additional information about the process of compiling the code from the question – I'm moving it here.
std::vector<int>
looks to the compiler just like std::vector<int, std::allocator<int>>
, i.e. as a type with TWO template parameters. The compiler, trying to match this type with the template specified in the function signature, stumbles upon a mismatch in the number of parameters and gets very upset about this. Thus, it is necessary to match the number of template parameters with the corresponding template template parameter. And it is worth remembering that default arguments do not cease to be arguments – for the compiler they are no worse than explicit ones!
As for the behavior of GCC, this is a deviation from the standard (well, or a bug). Does n't compile on GodBolt and GCC 8.3.