An answer to C++14 Variable Templates: what is the purpose? Any usage example? proposes a usage example of variable templates + generic lambdas that would look something like this:
void some_func() {
template<typename T>
std::map<int, T> storage;
auto store = []<typename T>(int key, const T& value) { storage<T>.insert(key, value) };
store(0, 2);
store(1, "Hello"s);
store(2, 0.7);
// All three values are stored in a different map, according to their type.
}
Unfortunately it doesn't compile so I've tried to "fix" it and this is my attempt so far.
#include <map>
template<typename T>
std::map<int, T> storage;
void some_func() {
auto store = [](int key, const auto& value) { storage<decltype(value)>.insert(key, value); };
store(0, 2);
store(1, std::string("Hello"));
store(2, 0.7);
}
The error message are:
main.cpp:7:76: error: no matching member function for call to 'insert'
auto store = [](int key, const auto& value) { storage<decltype(value)>.insert(key, value); };
~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
main.cpp:10:10: note: in instantiation of function template specialization 'some_func()::<anonymous class>::operator()<std::basic_string<char> >' requested here
store(1, std::string("Hello"));
When you instantiate a variable template, like all templates, each variable will be a different type. The theory is auto
doesn't get deduced for each type, but only one type initially (double). Therefore the code is invalid. Even if this could work, each instantiation of storage would refer to a different variable.
How can this code be rewritten to achieve the original intent?
Edit I made a small mistake in my edit (see revision history to avoid wall of text.) decltype(pair)
should be decltype(pair.second)
since there is only one template argument for storage
.
#include <map>
template <typename T>
std::map<int, T> storage;
void some_func() {
auto store = [&](auto pair) { storage<decltype(pair.second)>.insert(pair); };
store(std::pair<int, int>(0, 1));
store(std::pair<int, std::string>(1, "Hello!"));
store(std::pair<int, int>(2, 3));
}
int main()
{
}
There are now linker errors.
/tmp/main-5f1f7c.o: In function `some_func()':
main.cpp:(.text+0x1a): undefined reference to `storage<int>'
main.cpp:(.text+0x43): undefined reference to `storage<std::string>'
main.cpp:(.text+0x74): undefined reference to `storage<int>'
In order to fix the linker errors, I think you need to explicitly instantiate the arguments? (I'm not even sure if that's the correct term here.)
template <typename T>
std::map<int, T> storage;
template <>
std::map<int, int> storage<int>;
template <>
std::map<int, std::string> storage<std::string>;