I came up with this after answering this question
I had a simple function template (C++11):
template<class elem_t, class list_t>
bool in_list(const elem_t& elem, const list_t& list) {
for (const auto& i : list) {
if (elem == i) {
return true;
}
}
return false;
}
But GCC emitted warnings because it doesn’t seem to like deducing a template parameter as a std::initializer_list. So, without thinking, I made a specialization:
template<class elem_t>
bool in_list(const elem_t& elem, std::initializer_list<elem_t> list) {
for (const auto& i : list) {
if (elem == i) {
return true;
}
}
return false;
}
This worked. No more warnings. But when I looked again and thought about it, I remembered that C++ does not support partial template specialization on function templates. But that is what this appears to be. My only guess is that this is allowed because std::initializer_list is still dependent upon the template parameter, so it is, in essence, a different template. But I’m not sure if this is how it is supposed to be (isn’t there a gotw about templates not overloading?).
Is it standard behavior to accept this? And why?
And as a bonus question, why does GCC not like deducing a template parameter as a std::initializer_list? It seems quite silly to expect me to copy and paste the code and just replace the parameter with a std::initializer_list.
The warning message:
test.cpp: In function ‘int main()’:
test.cpp:33:43: warning: deducing ‘const list_t’ as ‘const std::initializer_list<int>’ [enabled by default]
test.cpp:6:6: warning: in call to ‘bool in_list(const elem_t&, const list_t&) [with elem_t = int, list_t = std::initializer_list<int>]’ [enabled by default]
test.cpp:33:43: warning: (you can disable this with -fno-deduce-init-list) [enabled by default]
When called by in_list(3, {1, 2, 3, 4, 5});
EDIT: Apparently deducing a template parameter as an initializer_list is an extension according to the working draft for my version of GCC (cite). So new question: Is this still an extension as of the final c++11 standard? If so, this would mean that it would be necessary for me to add the second function for standards-compliant code. Thanks for all your help!
EDIT2: The compiler dialect flag appears to be removed for GCC 4.7, so it seems like the issue was resolved, but I don’t know how it was resolved.
Using what @Ben Voigt said in the comments on the other answer, I have gathered some relevant standard quotes:
So that rules out function template specialisation as what you’re doing, because even if two function template overloads could potentially generate the same function, it’s not specialisation. So it’s overloading.
So they’re distinct functions and that’s why it’s not erroring.
This is priming is for what we both already saw, which is that
in_list(a, b)wherebis aninitializer_listappears to match both function templates.(*Note that "function template specialization" here doesn’t mean specialising a function template, it means a function template that has been instantiated with a type. So with
template<typename T> f();,f<int>()is a function template specialisation.)So we use what is called partial ordering of overloaded function templates to resolve this:
Ok, so that’s when partial ordering is for. This is what it does:
And then you get into the long and laborious process of determining which template is more specialised, which you can read about if you want, but it’s really complicated and I probably don’t understand it all (and plus, I don’t have enough time to write about it :)).