Reputation: 331
Its simplified version of my real problem. Why in the first case template specialization doesn't work?
If I swap first two functions then it will work.
Strange, but it works with msvc 19...
#include <string>
#include <vector>
#include <iostream>
template<typename T, typename M>
void write_impl(T &result, M m) {
for (const auto &i : result) {
write_impl(i, m);
}
}
template<typename M>
void write_impl(const std::string &result, M m) {
std::cout << result;
}
template<typename T>
void write_impl_1(T &result) {
for (const auto &i : result) {
write_impl_1(i);
}
}
template<>
void write_impl_1(const std::string &result) {
std::cout << result;
}
int main() {
std::vector<std::string> a{"42", "43", "44"};
write_impl(a, 42); // compile time error
write_impl_1(a); // works fine
}
Upvotes: 2
Views: 321
Reputation: 19113
First, interesting find! Took me a moment.
Second, there is no such thing as partial specialization of function template.
Full specialization is OK, so the second implementation will chose the specialization.
The first case is an overload resolution between two template functions, the more specialized will be chosen. The compiler will first construct the overload list - with candidates for the call.
Here's the thing, void write_impl(const std::string &result, M m)
won't be considered because it is not defined yet! The point of instantiation is directly after the template's definition. So, only the first template is in the overload set, it will match because a string is iterable and the instantiation will fail because char
is not.
This will raise the same error about foo
:
#include <string>
#include <vector>
#include <iostream>
template<typename T>
void bar(T &result) {
foo();
}
void foo(){}
int main() {
std::vector<std::string> a{"42", "43", "44"};
bar(a);
}
Why does the second case work then? Because the compiler will "look ahead" to see all possible specializations. But they still have to be present at the point which would instantiate the specialization. So, my belief here is that the second case is undefined behaviour, based on this answer. I am not very well versed in this dark corner of C++, I might be wrong here.
Why does MSCV work? Beats me, it is sometimes "special", maybe their point of instantiation is wrong, foo
will incorrectly work too.
Upvotes: 2
Reputation: 66190
The first case isn't a case of template specialization; it's function overloading.
Doesn't works because the first function call the second one that isn't declared. Switching the order, as you can see, works because the first (now second) know the declaration (and the definition too) of the second (now first).
You can't have template partial specialization for functions in C++; only full specialization.
You really need a sort of partial specialization, you can pass through a class/struct and a function inside it. You can partially specialize the struct/class.
For example
#include <string>
#include <vector>
#include <iostream>
template <typename T, typename M>
struct foo
{
static void bar (T & result, M m)
{
for (const auto &i : result)
foo<decltype(i), M>::bar(i, m);
}
};
template <typename M>
struct foo<std::string const &, M>
{
static void bar (std::string const & result, M)
{ std::cout << result; }
};
int main()
{
std::vector<std::string> a{"42", "43", "44"};
foo<decltype(a), int>::bar(a, 42);
}
But, as you can see, isn't really handy.
Obviously, if you don't need a partial-specialization-for-function emulation, and you are comfortable with different and overloaded template function (given that are calling each other) you can declare the first, declare and define the second and define the first;
Something as
template <typename M>
void write_impl (std::string const &, M);
template<typename T, typename M>
void write_impl (T & result, M m)
{
for (const auto &i : result)
write_impl(i, m);
}
template <typename M>
void write_impl (std::string const & result, M m)
{ std::cout << result; }
Upvotes: 2