I'm experimenting with C++ templates, and a kind of heterogenous type-safe map. Keys go with specific types. An example use would be something like a CSS stylesheet. I've got it to where I can things write:
styles.set<StyleKey::fontFamily>("Helvetica");
styles.set<StyleKey::fontSize>(23.0);
That type checks as desired; it won't compile calls where the key does not match it's intended value type. But I'm wondering if there's also a way to write it like this:
styles.set(StyleKey::fontFamily, "Helvetica");
styles.set(StyleKey::fontSize, 23.0);
... and have it deduce the same thing, because the first argument is a constant.
Here's my flailing attempt, pasted below and on godbolt. The set2 template does not work.
#include <iostream>
#include <string>
using namespace std;
struct color {
  float r,g,b;
};
ostream &operator <<(ostream &out, const color &c) {
  return out << "[" << c.r << ',' << c.g << ',' << c.b << "]";
}
// Gives something that would have types: string, float, color, bool
enum class StyleKey {
  fontFamily = 1, fontSize, fontColor, visible
};
template <StyleKey key>
struct KeyValueType {
};
struct StyleMap;
template <>
struct KeyValueType<StyleKey::fontFamily> {
  typedef string value_type;
  static void set(StyleMap *sm, value_type value);
};
template <>
struct KeyValueType<StyleKey::fontSize> {
  typedef float value_type;
  static void set(StyleMap *sm, value_type value);  
};
struct StyleMap {
  string fontFamily = "";
  float fontSize = 14;
  color fontColor = color{0,0,0};
  bool visible = true;
  template <StyleKey key>
  void set(typename KeyValueType<key>::value_type value) {
    cout << "set " << (int)key << " value: " << value << endl;
    KeyValueType<key>::set(this, value);
  }
  template <StyleKey key>
  void set2(StyleKey key2, typename KeyValueType<key>::value_type value) {
    static_assert(key == key2);
    cout << "set " << (int)key << " value: " << value << endl;
  }
};
void KeyValueType<StyleKey::fontFamily>::set(StyleMap *sm, string str) {
  sm->fontFamily = str;
}
void KeyValueType<StyleKey::fontSize>::set(StyleMap *sm, float sz) {
  sm->fontSize = sz;
}
void print(const StyleMap &sm) {
  cout << "font family : " << sm.fontFamily  << endl;
  cout << "font size   : " << sm.fontSize    << endl;
  cout << "color       : " << sm.fontColor   << endl;
  cout << "visible     : " << sm.visible     << endl;  
}
int main() {
  // Goal:
  //
  // StyleMap styles;
  // styles[fontFamily] = "Helvetica";
  // styles[fontSize] = 15.0;
  // string fam = styles[fontFamily]
  // float sz   = styles[fontSize];
  
  StyleMap styles;
  // This works!
  styles.set<StyleKey::fontFamily>("Helvetica");
  styles.set<StyleKey::fontSize>(23.0);
  // This won't compile, as desired
  // styles.set<StyleKey::fontFamily>(20);
  // But can we write it like this?
  // styles.set2(StyleKey::fontFamily, "Helvetica");
  // styles.set2(StyleKey::fontSize, 23.0);  
  print(styles);
}
 
    