Reputation: 4896
I want to be able to initialize container
with a brace init list and not have to specify types. This is more difficult as I have nested initializers. Now I'm at the point where it almost works, but there are two overloads for initializer_list in the variant wrapper and the compiler can't figure out which to use.
Actually in this case the call is not even ambiguous as only class keyval
takes a string as its first argument, which means the keyval overload of the variant wrapper should be chosen. But how can I tell this to the compiler?
My code (CompilerExplorer):
#include <string>
#include <variant>
template <typename... T> struct vwrapper;
using val = vwrapper<std::monostate, struct array, struct obj, int, bool>;
template <typename... T>
struct vwrapper : public std::variant<T...>
{
vwrapper(int);
vwrapper(bool);
vwrapper(std::initializer_list<struct keyval>);
vwrapper(std::initializer_list<val>);
// vwrapper(obj);
// vwrapper(array);
vwrapper(std::monostate);
};
struct obj
{
obj() = default;
obj(std::initializer_list<struct keyval> init) {
}
};
struct array
{
array() = default;
array(std::initializer_list<val> init) {
}
};
struct keyval
{
keyval() = default;
keyval(std::string, val);
};
template <typename... T>
vwrapper<T...>::vwrapper(int) {}
template <typename... T>
vwrapper<T...>::vwrapper(bool) {}
template <typename... T>
vwrapper<T...>::vwrapper(std::initializer_list<val>) {}
template <typename... T>
vwrapper<T...>::vwrapper(std::initializer_list<struct keyval>) {}
// template <typename... T>
// vwrapper<T...>::vwrapper(obj) {}
// template <typename... T>
// vwrapper<T...>::vwrapper(array) {}
keyval::keyval(std::string str, val value) { }
struct container : public array, public obj
{
using array::array;
using obj::obj;
};
int main()
{
container some_container = { 1, 2, true, false, 2, { { "hello", 2 }, { "mogli", true }}};
}
Upvotes: 0
Views: 120
Reputation: 21160
A minimal version of your snippet is
#include<initializer_list>
struct val
{
val(int);
val(bool);
val(std::initializer_list<struct keyval>);
val(std::initializer_list<val>);
};
struct keyval
{
keyval(const char*, val);
};
struct container
{
container(std::initializer_list<val>);
container(std::initializer_list<keyval>);
};
void foo()
{
container{{"", 2}, {"", true}};
}
At which point the ambiguity presents itself: a string literal is convertible to a bool
. Both val{"", 2}
and keyval{"", 2}
are valid expressions, with the first being interpreted as
val{std::initializer_list<val>{val{bool("")}, val{2}}}
To avoid the ambiguity, force the type to be keyval
container{keyval{"", 2}, {"", true}};
Upvotes: 2