How to initialize array<variant<customClass>, N> with initializer list?

Viewed 161

I met a wired problem that my user-defined-constructor can only work on a non-array std::variant.

Here is the code:

template<size_t N>
struct StringLiteral
{
    constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }

    char value[N];
};

template<StringLiteral _name, typename... ArgTys>
struct Message
{
    static_assert(sizeof(_name.value) <= 11U, "Name of message must less than 11 characters.");

    using MsgArg = std::variant<ArgTys...>;

    static constexpr auto NAME = _name.value;
    static constexpr auto SIZE = (sizeof(ArgTys) + ...);
    static constexpr auto COUNT = sizeof...(ArgTys);

    static void Register(void)
    {
        cout << "size of the message \"" << NAME << "\" is " << SIZE << '.' << endl;
    }

    static void Cast(int iDest, const Vector& vecOrigin, void* pClient, ArgTys... args)
    {
        auto f = [](auto&& arg)
        {
            using T = std::decay_t<decltype(arg)>;

            if constexpr (std::is_same_v<T, int>)
                std::cout << "int sent: " << arg << '\n';
            else if constexpr (std::is_same_v<T, float>)
                std::cout << "float sent: " << arg << '\n';
            else if constexpr (std::is_same_v<T, const char*>)
                std::cout << "string sent: " << std::quoted(arg) << '\n';
            else if constexpr (std::is_same_v<T, Vector>)
                cout << "vector sent: " << endl << arg;
            else if constexpr (std::is_same_v<T, short>)
                std::cout << "short sent: " << arg << '\n';
            else if constexpr (std::is_same_v<T, unsigned char>)
                std::cout << "byte sent: " << arg << '\n';
            else
                static_assert(always_false_v<T>, "Unknow type was sent!");
        };

        cout << "Cast message to " << iDest << " at " << endl << vecOrigin << " for " << pClient << endl;

        std::array<MsgArg, COUNT> rgArgs = { args... };

        for (auto& arg : rgArgs)
        {
            std::visit(f, arg);
        }
    }
};

int main()
{
    using gmsgGiveWpn = Message<"GiveWpn", unsigned char, Vector, const char *, short>;

    std::array<gmsgGiveWpn::MsgArg, 2> v = { Vector(1, 1, 1), 0U };

    return EXIT_SUCCESS;
}

With the custom class type 'class Vector' has constructor:

    constexpr Vector(Vector&& s) = default;
    Vector& operator=(const Vector& s) = default;
    Vector& operator=(Vector&& s) = default;
    constexpr Vector() : x(0), y(0), z(0) {}
    constexpr Vector(Arithmetic auto X, Arithmetic auto Y, Arithmetic auto Z) : x(static_cast<vec_t>(X)), y(static_cast<vec_t>(Y)), z(static_cast<vec_t>(Z)) {}
    constexpr Vector(const Vector2D& v2d, Arithmetic auto Z) : x(v2d.x), y(v2d.y), z(static_cast<vec_t>(Z)) {}
    template<Arithmetic T, std::size_t size> requires(size >= 3U) explicit constexpr Vector(const T(&rgfl)[size]) : x(static_cast<vec_t>(rgfl[0])), y(static_cast<vec_t>(rgfl[1])), z(static_cast<vec_t>(rgfl[2])) {}

Compile with VS2022/C++20, MSVC gave me the following error exact on the line where I put my packed arguments in the initializer brackets:

error C2440: 'initializing': cannot convert from 'Vector' to '_Ty'
        with
        [
            _Ty=std::variant<unsigned char,Vector,const char *,short>
        ]
message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

However, when I tried

gmsgGiveWpn::MsgArg v = Vector(0, 0, 0);

It just works as all I intended.

What should I do to define a proper constructor to make my Vector works in initializer-list?

Edit:
Tested on default array, i.e. MsgArg rg[] = {Vector(), 2};, does not work.
Tested adding an extra layer of {} surrounding the initializer list, i.e. std::array<MsgArg, COUNT> rgArgs = { {args}... };, does not work.

0 Answers
Related