I have some code that reads like this SSCCE:
#include <map>
#include <string>
#include <functional>
using std::map;
using std::string;
using std::function;
using std::forward;
template<typename ToType, typename... FromTypes>
ToType construct(FromTypes&&... fromTypes) {
return ToType(forward<FromTypes>(fromTypes)...);
}
class Variant {
public:
map<string, Variant> toMap() const;
};
map<string, Variant> Variant::toMap() const {
return {
{"test", {}},
{"test1", {}},
};
}
// #1
template <typename MapType>
MapType variantToMap(Variant const& v) {
// I expect an error on the following line because it's referencing the function labeled #4 which has not been declared yet.
return variantToMap<MapType>(v, construct<typename MapType::key_type, string>, construct<typename MapType::mapped_type, Variant>);
}
// #2
template <typename MapType>
MapType variantToMap(Variant const& v, function<typename MapType::key_type(string)> const& keyConvert) {
// I expect an error on the following line because it's referencing the function labeled #4 which has not been declared yet.
return variantToMap<MapType>(v, keyConvert, construct<typename MapType::mapped_type, Variant>);
}
// #3
template <typename MapType>
MapType variantToMap(Variant const& v, function<typename MapType::mapped_type(Variant)> const& valueConvert) {
// I expect an error on the following line because it's referencing the function labeled #4 which has not been declared yet.
return variantToMap<MapType>(v, construct<typename MapType::key_type, string>, valueConvert);
}
// #4
template <typename MapType>
MapType variantToMap(Variant const& v, function<typename MapType::key_type(string)> const& keyConvert, function<typename MapType::mapped_type(Variant)> const& valueConvert) {
MapType res;
for (auto pair : v.toMap()) {
res[keyConvert(pair.first)] = valueConvert(pair.second);
}
return res;
}
int main() {
Variant a;
// #1
variantToMap<map<string, Variant>>(a);
// #2
{
int counter = 0;
variantToMap<map<int, Variant>>(a, [&counter](string) -> int { return ++counter; });
}
// #3
variantToMap<map<string, int>>(a, [](Variant) -> int { return 42; });
// #4
{
int counter = 0;
variantToMap<map<int, int>>(a, [&counter](string) -> int { return ++counter; }, [](Variant) -> int { return 42; });
}
return 0;
}
Which compiles and works fine.
However, because it's declared out of order, I would expected this to have thrown errors. I assume that the reason why it's not is related to the fact that these functions are all templated. However, this is still a bit confusing to me because (for instance) the call to variantToMap #4 within variantToMap #1 has 3 arguments, which means
Is Clang and GCC inappropriately accepting these functions, or is this allowed? And if it is allowed, what's the rationale for it?
It should be noted that I already have reordered these functions, I'm just curious why it worked in the first place.