c ++: variadi c перегрузка шаблонов и функций - PullRequest
1 голос
/ 24 февраля 2020

см. Пример ниже в реальном времени: https://onlinegdb.com/Hkg6iQ3ZNI

#include <iostream>
#include <utility>
#include <type_traits>

class A 
{
    public:
    A(int v=-10):v_(v){}
    void print()
    {
        std::cout << "called A: " << v_ << std::endl;
    }
    private:
    int v_;
};

void f(int v)
{
    std::cout << "called f: " << v << std::endl;

}


template<typename T,typename ... Args>
void run(A&& a,
         T&& t,
         Args&& ... args)
{
    a.print();
    t(std::forward<Args>(args)...);
}


template<typename T,typename ... Args>
void run(T&& t,
          Args&& ... args)
{
  run(A(),
      std::forward<T>(t),
      std::forward<Args>(args)...);
}

int main()
{
    int v_function=1;
    int v_a = 2;

    run(f,v_function);

    return 0;
}

Приведенный выше код компилируется, запускается и печатается (как и ожидалось):

называется A : -10

вызывается f: 1

, но если основная функция изменяется на:

int main()
{
    int v_function=1;
    int v_a = 2;

    run(f,v_function);

    // !! added lines !!

    A a(v_a);
    run(a,f,v_function);

    return 0;
}

, то компиляция завершается с ошибкой:

main. cpp: 30: 6: ошибка: нет совпадения для вызова '(A) (void (&) (int), int &)'

t (std :: forward (аргументы) ...);

~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

, что, по-видимому, указывает на то, что даже когда экземпляр A передается в качестве первого аргумента, вызывается функция перегрузки

void(*)(T&&,Args&&...) 

, а не

void(*)(A&&,T&&,Args&&...) 

Ответы [ 2 ]

3 голосов
/ 24 февраля 2020

С

template<typename T,typename ... Args>
void run(A&& a,
         T&& t,
         Args&& ... args)

a не ссылка на пересылку, а ссылка на значение. Это означает, что когда вы делаете run(a,f,v_function);, эта функция не будет выбрана, потому что a является lvalue, и они не могут быть привязаны к ссылкам rvalue. Есть два быстрых способа исправить это. Во-первых, используйте std::move на a, как

run(std::move(a),f,v_function);

, но это не здорово. a фактически не перемещается в функцию, поэтому вы как бы нарушаете принцип наименьшего удивления.

Второй вариант - сделать A в функции типом шаблона, чтобы она стала ссылкой для пересылки. и тогда вы можете ограничить его типом A как

template<typename A_, typename T,typename ... Args, std::enable_if_t<std::is_same_v<std::decay_t<A_>, A>, bool> = true>
void run(A_&& a,
         T&& t,
         Args&& ... args)
{
    a.print();
    t(std::forward<Args>(args)...);
}
0 голосов
/ 25 февраля 2020

Ваш код работает, если вы звоните run с rvalue.

Здесь можно воспроизвести пример.

Как уже печален Натан Оливер: void run(A&& a, T&& t, Args&& ... args) ожидает rvalue reference.

Основы c идеи rvalue reference: вы передаете rvalue функции (например, строковому литералу). Это значение будет скопировано в функцию. Эта работа не нужна. Вместо этого вы просто «перемещаете» ссылку на это значение, чтобы оно «принадлежало» другой части вашей программы. Конструкторы перемещения являются хорошей отправной точкой для понимания этой проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...