简体   繁体   中英

How can I disambiguate initializer_lists for constructor calls?

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 }}};
}

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}};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM