Even if it doesn't directly answer the question, you may have an alternative by using a compile-time map as shown here. There is no lookup overhead since everything is done at compile time.
By using std::string_view as key, you can achieve the same purpose you are looking for. Note that Parameter doesn't need anymore to hold the member name since it corresponds to the key of the map.
#include <string_view>
// https://stackoverflow.com/questions/61281843/creating-compile-time-key-value-map-in-c
template <class Key, class Value, int N>
class CTMap
{
public:
struct KV { Key key; Value value; };
constexpr Value operator[](Key key) const { return Get(key); }
private:
constexpr Value Get(Key key, int i = 0) const
{
return i == N ?
KeyNotFound() :
pairs[i].key == key ? pairs[i].value : Get(key, i + 1);
}
static Value KeyNotFound() // not constexpr
{
return {};
}
public:
KV pairs[N];
};
////////////////////////////////////////////////////////////////////////////////
struct Parameter {
int min;
int max;
};
constexpr CTMap<std::string_view, Parameter, 2> parameters
{{
{"A",{1,2} },
{"B",{3,4} }
}};
static_assert (parameters["A"].min == 1);
static_assert (parameters["A"].max == 2);
static_assert (parameters["B"].min == 3);
static_assert (parameters["B"].max == 4);
int main()
{
}
Demo
It should work starting with c++17
Update:
Following @InTheWonderlandZoo's comment, it may be possible to overcome the issue when the key doesn't exist.
You could for instance provide only a find method that both returns a boolean telling whether the key has been found and the value if found (or default constructed value).
The other option is to provide a at method that can throw an exception if the key is not found.
This post shows for instance how to use the fact that a constepxr may be used at compile or runtime. So if we throw an exception when the key doesn't exist, we can get a compilation error
#include <string_view>
#include <iostream>
#include <exception>
////////////////////////////////////////////////////////////////////////////////
template <class Key, class Value, int N>
struct CTMap
{
struct KV { Key key; Value value; };
constexpr auto find (Key key) const
{
for (int i=0; i<N; i++)
{
if (pairs[i].key == key) { return std::make_pair(true,pairs[i].value); }
}
return std::make_pair (false, Value{});
}
constexpr Value at (Key key) const
{
auto lookup = find (key);
if (not lookup.first) { throw std::out_of_range ("key not found"); }
return lookup.second;
}
KV pairs[N];
};
////////////////////////////////////////////////////////////////////////////////
struct Parameter {
int min;
int max;
};
constexpr CTMap<std::string_view, Parameter, 2> parameters
{{
{"A",{1,2} },
{"B",{3,4} }
}};
// The compiler will ignore the runtime part.
static_assert (parameters.at("A").min == 1);
static_assert (parameters.at("A").max == 2);
static_assert (parameters.at("B").min == 3);
static_assert (parameters.at("B").max == 4);
// The compiler will try to use the runtime part
// which will imply a compilation error.
// static_assert (parameters.at("C").max == 0);
static_assert (parameters.find("B").first == true);
static_assert (parameters.find("C").first == false);
int main()
{
constexpr auto pa = parameters.at("A");
// Same thing here: should have a compilation error
// constexpr auto pc = parameters.at("C");
try
{
std::cout << parameters.at("C").max << "\n"; // OUPS...
}
catch (std::out_of_range const& exc)
{
std::cout << exc.what() << '\n';
}
}
For instance, constexpr auto pa = parameters.at("A"); is valid but one will get a compilation error with constexpr auto pc = parameters.at("C");:
foo.cpp: In function 'int main()':
foo.cpp:58:38: in 'constexpr' expansion of 'parameters.CTMap<std::basic_string_view<char>, Parameter, 2>::at(std::basic_string_view<char>(((const char*)"C")))'
foo.cpp:23:35: error: expression '<throw-expression>' is not a constant expression
23 | if (not lookup.first) { throw std::out_of_range ("key not found"); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And if you try to use auto pc = parameters.at("C");, you won't have a compilation error but an exception will be thrown at runtime.
Demo
enum EnumType { FIRST = 0, SECOND = 1, };. Then you can reference that withParameter::FIRSTandParameter::SECOND.parametersshould be declared constexpr.