Аргументы переменных C ++ с использованием списков инициализаторов - PullRequest
1 голос
/ 29 апреля 2020

Предположим следующий код, который является крошечной заменой sprintf. (_Itoa и подобные ему просто использовались для краткости кода.)

#include <cstdlib>
#include <string>
class Arg {
public:
    Arg(const std::string& s) :m_str(s) {}
    Arg(const char* s) : m_str(s) {}
    Arg(int digi, double number)  {char buf[128]; m_str = _gcvt(number, digi, buf);}
    operator const std::string& ()const { return m_str; }
private:
    std::string m_str;
};

class Format {
public:
    Format(/*const char* format, */std::initializer_list<Arg> args); // see below
    const std::string& str()const { return m_str; }
private:
    std::string m_str;
};

Format::Format(/*const char* format, */std::initializer_list<Arg> args) {
    auto arg = args.begin();
    auto format = std::string(*arg++);
    for(const char* c = format.c_str(); *c!='\0'; ++c) {
        if(*c=='%') { m_str+=*arg++; }
        else { m_str+=*c; }
    }
}


int main() {

    std::string test1 = Format{"test Double:% String:%", {5, 456.78}, "foo"}.str();

    // I want to make this work. See the braces.
    std::string test2 = Format("test Double:% String:%", {5, 456.78}, "foo").str();

    return 0;
}

Видите ли, я хочу передать аргументы, ограниченные типом "Arg", но используйте конструктор, который использует, например, varadi c шаблонов вместо initializer_list <> для лучшей читабельности.

Я пытался:

    template<typename... T>
    Format(T&& ... args) : Format(std::forward<Args>(args)...) {}

Но я получаю:

error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'Format'
note: No constructor could take the source type, or constructor overload resolution was ambiguous

Ответы [ 3 ]

1 голос
/ 29 апреля 2020

Во-первых, вы должны использовать фигурные скобки в списке инициализатора элемента, чтобы переслать в конструктор, принимая std::initializer_list.

template<typename... T>
Format(T&& ... args) : Format{std::forward<T>(args)...} {}
//                           ^                        ^

Во-вторых, учитывая Format("test Double:% String:%", {5, 456.78}, "foo"), к сожалению, braced-init-list как {5, 456.78} не может быть выведено при выводе типа шаблона, у него нет типа. Вы можете указать тип явно как

std::string test2 = Format("test Double:% String:%", Arg(5, 456.78), "foo").str();
//                                                      ^         ^
1 голос
/ 29 апреля 2020

std::initializer_list требует {} не ().

{5, 456.78} не имеет типа и не может быть выведен для шаблона.

Способ сохранить ваш синтаксис в старый способ перегрузки:

Format(Arg arg0) : Format(std::initializer_list{arg0});
Format(Arg arg0, Arg arg1) : Format({arg0, arg1});
Format(Arg arg0, Arg arg1, Arg arg2) : Format({arg0, arg1, arg2});
// ... Up to some limit
0 голосов
/ 29 апреля 2020

Вы можете сделать так:

template<typename... T>
Format(T&& ... args) : Format({std::forward<T>(args)...}) {}}

Но при вызове вы должны вручную указать, что второй аргумент - Arg, например:

std::string test2 = Format("test Double:% String:%", Arg{5, 456.78}, "foo").str();

пример

...