Принятие вложенных шаблонов классов переменных в качестве аргументов для шаблона функции - PullRequest
1 голос
/ 22 сентября 2010

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

template<typename... Args> struct Entity {

    template<typename... InnerEntArgs> struct InnerEntity {
        InnerEntity(InnerEntArgs... inner_ent_args) {
            ... //do stuff w/ InnerEntArgs pack
            ... //do stuff that makes Inner dependent on Outer's Args pack
        }
    };
};

struct ThingA : Entity<int, string> {
    ... //construct ThingA
};

struct ThingB : Entity<string, string> {
    ... //construct ThingB
};

auto foo = my_func(
    ThingA::InnerEntity<int, int, int>(1, 2, 3)
    , ThingB::InnerEntity<string, int>("bar", 1)
);

Ниже приведен код, который я собрал вместе для функции, и он хорошо компилируется, ноЯ не уверен, правильно ли он настроен.В частности, я немного размышляю о том, как typename и ::template делают компилятор счастливым в этом контексте, или если эта функция будет вести себя так, как я ожидаю:

template<
    typename... ArgsA, typename... ArgsAInner
    , typename... ArgsB, typename... ArgsBInner
> auto my_func(
    typename Entity<ArgsA...>::template InnerEntity<ArgsAInner...> A
    , typename Entity<ArgsB...>::template InnerEntity<ArgsBInner...> B
) -> tuple<decltype(A), decltype(B)> {
    return make_tuple(A, B);
}

I думаю, Я хорошо понимаю, как пакеты / параметры выводятся / выводятся, и как auto, decltype, и конечный тип возврата делают свое дело, но если я ошибаюсь, пожалуйста,дайте мне знать, как это сделать.

Кроме того, если кому-то захочется продемонстрировать вариационную версию этой функции, которая может принять любое количество вложенных шаблонов вариационных классов и поместить их в подходящий контейнер или структуру данных, этоотлично, но меня в первую очередь интересует полное понимание typename и ::template.Спасибо заранее!

* Если я неправильно сформулировал этот заголовок или я перепутал термины, объясните, пожалуйста.:) Я здесь, чтобы учиться.

1 Ответ

4 голосов
/ 22 сентября 2010

Это не будет работать, потому что Entity<Args>::InnerEntity - это не выводимый контекст. Означает, что ArgsA... и ArgsAInner... не могут быть выведены, также как и для другого параметра. Это потому, что прежде чем компилятор сможет вывести Args, он должен знать, к какому типу InnerEntity принадлежит член, но чтобы знать , что , он должен вывести Args.

Вы можете поместить эту функцию в качестве шаблона функции друга в Entity<Args...> и заставить ее работать, если оба они являются членами одного и того же шаблона. Но в прошлый раз, когда я проверял, GCC не нашел функции друзей, определенные в шаблонах классов.

template<typename ...Args>
class Entity {
  template<typename ...ArgsInner>
  class InnerEntity {

  };

  template<typename ...ArgsAInner, typename... ArgsBInner>
  > friend auto my_func(
        InnerEntity<ArgsAInner...> A
      , InnerEntity<ArgsBInner...> B
  ) -> tuple<decltype(A), decltype(B)> {
      return make_tuple(A, B);
  }

};

Вы также можете объявить некоторый член typedef в InnerEntity, который указывает тип внешнего класса, и сформулировать my_func в терминах этого, чтобы SFINAE мог разобрать его для нечленов.

template<typename ...Args>
class Entity {
  template<typename ...ArgsInner>
  class InnerEntity {
    typedef Entity outer_entity;
  };    
};

template<typename A, typename B, typename Result>
struct require_entity { };

template<typename ...ArgsA, typename ...ArgsB, typename Result>
struct require_entity<Entity<ArgsA...>, Entity<ArgsB...>> {
   typedef Result type;
};

template<template<typename...> class AInner, template<typename...> class BInner, 
         typename ...ArgsAInner, typename ...ArgsBInner>
> auto my_func(
      AInner<ArgsAInner...> A
    , BInner<ArgsBInner...> B
) -> typename require_entity<
         typename AInner<ArgsAInner...>::outer_entity, 
         typename BInner<ArgsBInner...>::outer_entity, 
           tuple<decltype(A), decltype(B)>>::type 
{
    return make_tuple(A, B);
}

Конечно, вам не нужна эта template<typename...> class AInner вещь, если вам не нужен доступ к ArgsAInner типам, как в приведенном выше my_func. В таком случае вам лучше просто принять typename AInner и меньше писать. SFINAE по-прежнему будет следить за тем, чтобы было принято только правильное.

...