Разверните пакет параметров при вызове методов базовых типов, когда класс шаблона переменной наследуется от аргументов шаблона. - PullRequest
0 голосов
/ 28 декабря 2018

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

Я пытался написать это, но он выигралt compile:

class Foo
{
public:
    void method()
    {
        std::cout << "Foo::method()\n";
    }
};

class Bar
{
public:
    void method()
    {
        std::cout << "Bar::method()\n";
    }
};

template <typename... Ts>
class Combined: public Ts...
{
public:
    Combined(const Ts&... ts): Ts(ts)... {}
    Combined(Ts&&... ts): Ts(std::move(ts))... {}

    template <typename U>
    void call_methods()
    {
        U::method();
    }

    template <typename U, typename... Us>
    void call_methods()
    {
        U::method();
        call_methods<Us...>();
    }

    void method()
    {
        call_methods<Ts...>();
    }
};


int main(int argc, char *argv[])
{
    Combined<Foo, Bar> obj({}, {});
    obj.method();
    return 0;
}

Компилятор говорит следующее:

test.cpp:42:9: error: call to member function 'call_methods' is ambiguous
        call_methods<Us...>();
        ^~~~~~~~~~~~~~~~~~~
test.cpp:47:9: note: in instantiation of function template specialization
      'Combined<Foo, Bar>::call_methods<Foo, Bar>' requested here
        call_methods<Ts...>();
        ^
test.cpp:57:9: note: in instantiation of member function
      'Combined<Foo, Bar>::method' requested here
    obj.method();
        ^
test.cpp:33:10: note: candidate function [with U = Bar]
    void call_methods()
         ^
test.cpp:39:10: note: candidate function [with U = Bar, Us = <>]
    void call_methods()
         ^

По существу, существует неопределенность между call_methods<U = Bar> и call_methods<U = Bar, Us = <>>.Но если я объявлю void call_methods() {}, он по какой-то причине не будет соответствовать * 1012. *

Если пока не ясно, я хочу, чтобы Combined<Foo, Bar>::method() позвонил Foo::method() и Bar::method().

Я знаю, что, вероятно, смогу реализовать это, имея tuple с объектами соответствующих типов в качестве члена и просто перебирая их, но я действительно хочу найти решение, которое ближе к тому, что я написал.

Ответы [ 4 ]

0 голосов
/ 28 декабря 2018

Чтобы исправить решение, отключите вторую перегрузку, когда пакет параметров пуст:

template <typename U, typename... Us>
typename std::enable_if< (sizeof...(Us) > 0) >::type
call_methods()
{
    U::method();
    call_methods<Us...>();
}

Избавляется от неоднозначности.

Пример в реальном времени.

0 голосов
/ 28 декабря 2018

Одно из решений:

template <typename... Ts>
class Combined: public Ts...
{
public:
    Combined(const Ts&... ts): Ts(ts)... {}
    Combined(Ts&&... ts): Ts(std::move(ts))... {}

    void method()
    {
        int dummy[] { (Ts::method(), 0)... };
    }
};

Не идеально, но должно быть так же эффективно, как моя оригинальная идея.

0 голосов
/ 28 декабря 2018

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

Создание экземпляра foo<Bar>() неотличимо от разрешения перегрузки ( тот, у которого один параметр, или тот, у кого пустой пакет параметров? ), следовательно, он заканчивается неопределенностью в вызове.

Как уже упоминалось в ответе SergeyA Чтобы решить эту проблему, нужно иметь только одну перегрузку и выполнить in-site расширение вызова.

0 голосов
/ 28 декабря 2018

Существует несколько решений этой проблемы.Проще всего было бы расширить вызов на месте, а не рекурсивно.Что-то вроде следующего:

struct Foo
{
    void method();
};

struct Bar
{
    void method();
};

template <typename... Ts>
class Combined: public Ts...
{
public:
    Combined(const Ts&... ts);
    Combined(Ts&&... ts);

    void method()
    {

        bool z[] = { (Ts::method(), true)... };
        (void)z;
    }
};


int main(int argc, char *argv[])
{
    Combined<Foo, Bar> obj({}, {});
    obj.method();
    return 0;
}
...