C++ fold выражение с пользовательским классом

Я хочу создать функцию журнала с несколькими параметрами, а также вызвать функцию журнала с помощью своего класса.
Вот мой код (компилятор: Visual Studio 2019 или x86-64 gcc9.2)

Вопрос 1> Я не могу понять функцию журнала. Можно ли использовать такое выражение fold? (эта функция взята из библиотеки spdlog)

Вопрос 2> Как я могу использовать функцию журнала с классом Mystruct?
log(1, MyStruct(1, 1.1f, "hello world"s)); // compiler error

#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>

template<typename ... Args>
void log(int level, Args const& ... args)
{
    std::ostringstream stream;
    using List = int[];
    (void)List {
        0, ((void)(stream << args), 0) ...
    };
    std::cout << stream.str() << std::endl;
}

class MyStruct
{
public:
    int val1 = 0;
    float val2 = 0.f;
    std::string val3;

    MyStruct(int v1, float v2, std::string_view const& v3) : val1(v1), val2(v2), val3(v3) {};
    std::string to_string() const
    {
        std::stringstream stream;
        stream << "val1=" << val1 << ", val2=" << val2 << ",val3=" << val3;
        return stream.str();
    }
};

std::ostringstream& operator<< (std::ostringstream& stream, MyStruct&& val)
{
    auto str = val.to_string();
    std::operator <<(stream, str);
    return stream;
}

void work_good()
{
    using namespace std::string_literals;  
    log(1, 1.1f, "hello world"s);
}

void compile_error()
{
    using namespace std::string_literals;  
    log(1, MyStruct(1, 1.1f, "hello world"s));
}

int main()
{
    work_good();
}

person Eric Kim    schedule 19.02.2020    source источник
comment
Свернутое выражение С++ 17 будет: ((stream << args), ...); или (stream << .. << args);   -  person Jarod42    schedule 19.02.2020


Ответы (2)


Вопрос 1> Я не могу понять функцию журнала. Можно ли использовать такое выражение fold?

Чтобы быть точным, он не использует кратное выражение (начиная с C++17). . См. расширение пакета, которое поддерживалось, начиная с C++11, и да, это действительное использование, как в log.

Шаблон, за которым следует многоточие, в котором имя хотя бы одного пакета параметров появляется хотя бы один раз, расширяется до нуля или более экземпляров шаблона, разделенных запятыми, где имя пакета параметров заменяется каждым из элементов. из пачки по порядку.

а также

Вопрос 2> Как я могу использовать функцию журнала с классом Mystruct?

Вы объявили operator<<, взяв MyStruct в качестве rvalue-reference, но в log аргументы передаются в operator<< как lvalue, которое не может быть привязано к rvalue-reference. Вы можете изменить operator<< на использование ссылки lvalue на const, что применимо как для lvalue, так и для rvalue. например

std::ostringstream& operator<< (std::ostringstream& stream, const MyStruct& val)
{
    auto str = val.to_string();
    std::operator <<(stream, str);
    return stream;
}

ПРЯМОЙ ЭФИР

person songyuanyao    schedule 19.02.2020
comment
@eeorika Не могли бы вы объяснить подробнее? Я пробовал код OP, который мог компилироваться в режиме до C++ 17. - person songyuanyao; 19.02.2020
comment
Действительно? Я могу быть сбит с толку. - person eerorika; 19.02.2020
comment
@eerorika Я думаю, что этот код применяет выражение fold и код OP этого не делает. Обратите внимание на предупреждение от Clang. - person songyuanyao; 19.02.2020
comment
Я не знал, что этот оператор запятой можно использовать даже с пакетным расширением. Я только когда-либо видел расширение пакета со списками аргументов. Хорошо знать. - person eerorika; 19.02.2020
comment
@songyuanyao Спасибо, вы правы Мне удалось создать этот код, лол. Кроме того, я исправил более общий код - person Eric Kim; 19.02.2020
comment
как прокомментировал @songyuanyao, функция журнала представляет собой расширение пакета‹br› Вы можете проверить расширенный код здесь - person Eric Kim; 19.02.2020

Я изменил некоторый код, на который ссылался ответ. Я хочу, чтобы функция журнала была более общей

namespace nlog
{
    class MyStruct
    {
    public:
        int val1 = 0;
        float val2 = 0.f;
        std::string val3;

        MyStruct(int v1, float v2, std::string_view const& v3) : val1(v1), val2(v2), val3(v3) {};
        std::string to_string() const
        {
            std::stringstream stream;
            stream << "val1=" << val1 << ", val2=" << val2 << ",val3=" << val3;
            return stream.str();
        }
    };

    template < typename T, typename decltype(std::declval<T>().to_string())* = nullptr>
        std::ostream& operator<< (std::ostream& stream, T&& val)
    {
        auto str = val.to_string();
        std::operator <<(stream, str);
        return stream;
    }


    template <typename ... Ts>
    void tolog(Ts && ...args)
    {
        std::stringstream strbuf;
        (strbuf << ... << std::forward<Ts>(args));

        std::cout << strbuf.str() << std::endl;
    }

    template <typename ... Ts>
    void toout(Ts && ...args)
    {
        (std::cout << ... << args);
    }
};

namespace nlog
{
    void Test1()
    {
        using namespace std::string_literals;
        std::stringstream strbuf;
        strbuf << MyStruct(0, 0.1f, "Eric"s);
        std::cout << strbuf.str() << std::endl;
    }

    void Test2()
    {
        using namespace std::string_literals;
        tolog(1, 1.1f, "hello world"s);
    }

    void Test3()
    {
        using namespace std::string_literals;
        tolog("I like this func val=", 100, ", youvalue=", 1.0f, ", MyStruct=",  MyStruct(0, 0.1f, "Eric"s));
    }
};

int main()
{
    nlog::Test1();
    nlog::Test2();
    nlog::Test3();
}
person Eric Kim    schedule 19.02.2020