Сбивающее с толку упражнение с вариативными шаблонами

#c #function #c 11 #variadic-templates

Вопрос:

Я поставил перед собой эту задачу, чтобы помочь изучить вариативные шаблоны. Функция add_and_cat() должна сначала принимать пару<int,строка>, затем переменное число int или строк или смесь. Поскольку он сталкивается с ними рекурсивно, он должен вычесть каждое значение int из pair.first и объединить каждую строку в pair.second. Функция pp просто выводит пару.

Кажется, это работает во многих случаях, но в других я получаю ошибку компиляции, утверждающую, что нет соответствующей функции (внизу) … даже несмотря на то, что, насколько я могу судить, есть только один совершенно очевидный кандидат :/ Я играл и пытался разобраться в этом, но … пока не везет. Что я упускаю?

————————— Код: —————————-

 #include <iostream>

void pp(std::pair<int,std::string> printme) {
    std::cout << printme.first << " : " << printme.second << "n";
}

//base case int
//must be int or std::string for now
void add_and_cat(std::pair<int, std::string>amp; store, int i) {
    store.first -= i;
}

//base case string
void add_and_cat(std::pair<int, std::string>amp; store, std::string s) {
    store.second  = s;
}

//full case for int
template<typename ...Ts>
void add_and_cat(std::pair<int, std::string>amp; store, int i, Ts... rest) {
    store.first -= i;
    add_and_cat(store, rest...);
}

//full case for string
template<typename ...Ts>
void add_and_cat(std::pair<int, std::string>amp; store, std::string s, Ts... rest) {
    store.second  = s;
    add_and_cat(store, rest...);
}



int main()
{

    std::pair<int, std::string> p{0,"START"};

    //add_and_cat(p, 1, 2, 3, 4); pp(p);                    //fine
    //add_and_cat(p, 3, 4, 5, 6); pp(p);                    //fine
    //add_and_cat(p, "A", "B", "C", "D"); pp(p);            //fine
    //add_and_cat(p, "D", "E", "F", "G"); pp(p);            //fine
    //add_and_cat(p, "A", 1, "B"); pp(p);                   //fine
    //add_and_cat(p, 1, "A", 1, "B"); pp(p);                //compile error
    //add_and_cat(p, 1, 2, "A",3); pp(p);                   //compile error
    //add_and_cat(p, "A", 1, 2, "B"); pp(p);                //fine
    //add_and_cat(p, "A", 1, 2, "B","C"); pp(p);            //compile error
    //add_and_cat(p, 1, 2, "B","C"); pp(p);             //compile error



    return 0;
}

 

—————————— Ошибка: ——————————

 /mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp: In instantiation of ‘std::pair<int, std::__cxx11::basic_string<char> > add_and_cat(std::pair<int, std::__cxx11::basic_string<char> >amp;, int, Ts ...) [with Ts = {const char*, int, const char*}]’:
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:273:45:   required from here
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:250:20: error: no matching function for call to ‘add_and_cat(std::pair<int, std::__cxx11::basic_string<char> >amp;, const char*amp;, intamp;, const char*amp;)’
  250 |  return add_and_cat(store, rest...);
      |         ~~~~~~~~~~~^~~~~~~~~~~~~~~~
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:233:29: note: candidate: ‘std::pair<int, std::__cxx11::basic_string<char> > add_and_cat(std::pair<int, std::__cxx11::basic_string<char> >amp;, int)’
  233 | std::pair<int, std::string> add_and_cat(std::pair<int, std::string>amp; store, int i) {
      |                             ^~~~~~~~~~~
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:233:29: note:   candidate expects 2 arguments, 4 provided
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:240:29: note: candidate: ‘std::pair<int, std::__cxx11::basic_string<char> > add_and_cat(std::pair<int, std::__cxx11::basic_string<char> >amp;, std::string)’
  240 | std::pair<int, std::string> add_and_cat(std::pair<int, std::string>amp; store, std::string s) {
      |                             ^~~~~~~~~~~
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:240:29: note:   candidate expects 2 arguments, 4 provided
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:247:29: note: candidate: ‘template<class ... Ts> std::pair<int, std::__cxx11::basic_string<char> > add_and_cat(std::pair<int, std::__cxx11::basic_string<char> >amp;, int, Ts ...)’
  247 | std::pair<int, std::string> add_and_cat(std::pair<int, std::string>amp; store, int i, Ts... rest) {
      |                             ^~~~~~~~~~~
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:247:29: note:   template argument deduction/substitution failed:
/mnt/c/Users/Tim/Nextcloud/playground/seqan3/source/hello_world.cpp:250:28: note:   cannot convert ‘rest#0’ (type ‘const char*’) to type ‘int’
  250 |  return add_and_cat(store, rest...);
      |                            ^~~~
 

Большое спасибо за любую помощь!

Комментарии:

1. Мне нужно было добавить прямое объявление одной из функций, но тогда это работает для меня: godbolt.org/z/zK8oM6KKe

Ответ №1:

Он выходит из строя, когда int перегрузка должна вызывать string перегрузку, т. Е. Когда int параметр предшествует string единице. Вы должны объявить функцию, прежде чем сможете ее вызвать:

 #include <iostream>

//...

// declare "full case for string" called in "full case for int"
template<typename ...Ts>
void add_and_cat(std::pair<int, std::string>amp; store, std::string s, Ts... rest);

//full case for int
template<typename ...Ts>
void add_and_cat(std::pair<int, std::string>amp; store, int i, Ts... rest) {
    store.first -= i;
    add_and_cat(store, rest...);
}

//full case for string
template<typename ...Ts>
void add_and_cat(std::pair<int, std::string>amp; store, std::string s, Ts... rest) {
    store.second  = s;
    add_and_cat(store, rest...);
}


int main()
{
    std::pair<int, std::string> p{0,"START"};
    add_and_cat(p, 1, "A", 1, "B"); pp(p);             // no compile error
    add_and_cat(p, 1, 2, "A",3); pp(p);                // no compile error
    add_and_cat(p, "A", 1, 2, "B","C"); pp(p);        // no compile error
    add_and_cat(p, 1, 2, "B","C"); pp(p);             // no compile error
}
 

Живая демонстрация

Обратите внимание, что вы можете избежать рекурсии с помощью выражения сгиба:

 void add_one(std::pair<int,std::string>amp; store,int x){ store.first -=x; }
void add_one(std::pair<int,std::string>amp; store,const std::stringamp; x){ store.second  =x; }

template <typename ...Ts>
void add_and_cat2(std::pair<int,std::string>amp; store,Ts... t){
    (add_one(store,t),...);
}
 

Живая демонстрация