Вариативные шаблоны и динамическое приведение

У меня есть кусок кода C++ следующим образом:

template <typename ...A> 
struct CastAll{
  template <typename ...B>
  void cast_all(void(*fun)(B...), A...as){
    //...
  }
};

Что я хотел бы сделать, так это реализовать cast_all таким образом, чтобы он динамически приводил каждый из своих аргументов к соответствующему типу в B, а затем вызывал данную функцию fun с «приведенными» аргументами.

Например, в:

struct A{};

struct B : public A{};

void foo(B *b1, B *b2){
  //... does something with b1 and b2
}

int main(){

  A *a1 = new B();
  A *a2 = new B();

  CastAll<B*, B*> cast; //used to cast each A* to B*
  cast.cast_all<B*, B*>(foo, a1, a2);
}

cast_all должен расширяться до чего-то вроде: foo(dynamic_cast(a1), dynamic_cast(a2));

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

Любые идеи?


person Filipe Arcanjo    schedule 07.09.2011    source источник
comment
Действительно ли в этом есть необходимость? Мне трудно увидеть потребность. Я все же буду думать над ответом.   -  person Mooing Duck    schedule 08.09.2011
comment
Это позволило бы мне написать версию этой реализации множественной отправки на C++, которая будет работать с функции с произвольным количеством параметров.   -  person Filipe Arcanjo    schedule 08.09.2011


Ответы (2)


Просто

template <typename ...A> 
struct CastAll{
    template <typename ...B>
    void cast_all(void(*fun)(B...), A...as){
        fun(dynamic_cast<B>(as)...);
    }
};

должно работать, и это работает с моей копией GCC. Тем не менее, необходимы некоторые изменения в вашем примере кода: A должен быть полиморфным (что, в свою очередь, сделает B полиморфным), чтобы dynamic_cast был возможен (я добавил виртуальный деструктор по умолчанию, как это принято в моем примере кода); и вы, вероятно, намеревались использовать CastAll как:

CastAll<A*, A*> cast;
cast.cast_all(foo, &a1, &a2);

Другими словами, аргумент, который вы передаете cast_all, является указателем на A, который затем преобразуется в B внутри тела. Кроме того, выводятся некоторые параметры шаблона1.

Это работает, потому что вам разрешено использовать несколько пакетов параметров (здесь A и B) в одном расширении пакета (здесь dynamic_cast), при условии, что они имеют одинаковый размер; в противном случае это скрытая ошибка из-за SFINAE. Начиная с n3290, шаблоны Variadic 14.5.3/5 [temp.variadic]:

  1. [...] Шаблон расширения пакета должен называть один или несколько пакетов параметров, которые не расширяются путем расширения вложенного пакета; такие пакеты параметров в шаблоне называются нерасширенными пакетами параметров. Все пакеты параметров, расширенные расширением пакета, должны иметь одинаковое количество указанных аргументов. [...]

1: я не могу найти исчерпывающую информацию о том, разрешен ли здесь вывод; GCC даже может вывести оба пакета, если я превращу CastAll в полиморфный функтор. Я несколько сомневаюсь, что это вообще обязательное поведение, но, по крайней мере, вы, кажется, все равно знаете, как указать невыведенный аргумент.

person Luc Danton    schedule 08.09.2011
comment
Я рад, что кто-то знал ответ. Я бы порекомендовал сделать cast_all статическим, просто чтобы сохранить строку кода для ее создания. - person Mooing Duck; 08.09.2011

[EDIT] Переписано с нуля. Нечто подобное должно быть возможно, но у меня нет доступа к компилятору, который позволяет вариативным шаблонным функциям не быть концом, поскольку это больше не требуется. Это не удастся, если вы не передадите хотя бы один параметр, но я не вижу в этом проблемы.

template<typename...BL>
struct Recast {
    template <typename B, typename ...BR>
    struct Inner {
        template <typename A, typename ...AR>
        static void cast_all(void(*fun)(BL...,B,BR...), BL... al, A a, AR... ar) {
            Recast<BL..., B>::template Inner<BR...>::cast_all<AR...>(fun, al..., dynamic_cast<B>(a), ar..);
        }
    };
    template <typename B>
    struct Inner<B> {
        template <typename A>
        static void cast_all(void(*fun)(BL...,B), BL... al, A a) {
            fun(al..., dynamic_cast<B>(a));
        }
    };
};

template <typename ...BR>  //note I switched these
struct CastAll{
    template <typename ...AR>  //note I switched these
    static void cast_all(void(*fun)(BR...), AR...ar){
      Recast<>::template Inner<BR...>::cast_all(fun, ar...);
    }
};

struct A{};

struct B : public A{};

void foo(B *b1, B *b2){
  //... does something with b1 and b2
}

int main(){

  A *a1 = new B();
  A *a2 = new B();

  CastAll<B*, B*>::cast_all(foo, a1, a2);
}

Я признаю, что все еще есть ошибки, которые я не могу понять, как сообщает ideone.com.

prog.cpp: в статической функции-члене 'static void Recast::Inner::cast_all(void (*)(BL..., B, BR...), BL..., A, AR...)' :
prog.cpp:7:39: ошибка: ожидается основное выражение перед токеном '...'
prog.cpp:7:39: ошибка: ожидается ';' перед токеном '...'

person Mooing Duck    schedule 07.09.2011
comment
Я не думаю, что это сработает, потому что вызов fun() должен использовать динамически приведенный As (потому что он принимает B в качестве аргументов). - person SoapBox; 08.09.2011
comment
Отредактировано, но теперь в качестве не последнего параметра требуются пакеты параметров, и у меня нет доступа к компилятору, который это позволяет. - person Mooing Duck; 08.09.2011
comment
Clang++ 2.1 на компьютере Mac выдает следующую ошибку: tmp.cc:7:39: ошибка: ожидается '(' для приведения в стиле функции или построения типа Recast‹BL..., B›::Inner‹BR.. .›::cast_all‹AR...›(fun, al..., dynamic_cast‹B›(a), ar..); ~~^ Произошла 1 ошибка. - person Filipe Arcanjo; 08.09.2011
comment
ideone.com выдал ту же ошибку, но я не понимаю, почему. На данный момент я просто пытаюсь спасти свой ответ, насколько это возможно, потому что он единственный :( - person Mooing Duck; 08.09.2011
comment
Похоже, что мы должны добавить к оператору '::' префикс шаблона ключевого слова, потому что Inner‹› и cast_all также являются шаблонами. См. это: stackoverflow.com/questions/3786360/confusing-template-error . Однако clang++ по-прежнему сообщает о многих ошибках, которые я не могу исправить после этого :( - person Filipe Arcanjo; 08.09.2011
comment
Боже... попытался протестировать код с одним параметром шаблона для CastAll‹B*› и... clang: ошибка: невозможно выполнить команду: ошибка сегментации: 11 - person Filipe Arcanjo; 08.09.2011