Подтвердить что ты не робот

Как сделать ADSL С++ для просмотра всех экземпляров шаблона?

Я следую Как реализовать счетчик константных выражений в С++ Учебник И я пытаюсь исправить С++ 14 Размышления без макросов, разметки и внешнего инструментария. Ограничения.

Основная идея учебника такова:

template<int N>
struct flag {
  friend constexpr int adl_flag (flag<N>);
};

template<int N>
struct writer {
   friend constexpr int adl_flag (flag<N>) { return N; }
   static constexpr int value = N;
};

template<int N, class = char[noexcept(adl_flag(flag<N> ()))?+1:-1]>
int constexpr reader (int, flag<N>) { return N; }

template<int N>
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1>())) { return R; }

int constexpr reader (float, flag<0>) { return 0; }

template<int N = 1, int C = reader (0, flag<32> ())>
int constexpr next (int R = writer<C + N>::value) { return R; }

int main () {
  constexpr int a = next ();
  constexpr int b = next ();
  constexpr int c = next ();

  // YES! it works!!!
  static_assert (a == 1 && b == a+1 && c == b+1, "try again");
}

Примечание. Если вас сейчас не интересует, то пора прекратить чтение: -)

В обсуждении объясняется, как извлекать количество полей и типов полей для типов POD с использованием агрегатной инициализации и оператора неявного преобразования, но основное ограничение заключается в том, что поддерживаются только примитивные типы.

Я предоставил вышеуказанный фон, чтобы оправдать мои мотивы!

Когда я объединил эти два подхода, я пришел к следующему:

template<int N>
struct flag {
  friend constexpr int adl_flag (flag<N>);
};

template<typename T, int N>
struct writer {
  friend constexpr int adl_flag (flag<N>) {
    return N;
  }
  friend constexpr T field_type(flag<N>) { return T{}; }
  static constexpr int value = N;
};

field_type(flag<N>) даст мне тип N -го поля. Обратите внимание, что это функция друга и для N -го поля типа POD точно один field_type(flag<N>) будет определен компилятором.

g++ дает no matching function for call to 'field_type(flag<1>) для decltype(field_type(flag<1>)).

Мне нужно как-то заставить ADL искать во всех экземплярах writer<T,N>. Как я могу это сделать?

Обновление

Как @T.C.mentioned ADL рассматривает только связанные классы, а writer не является одним. (Вот почему adl_flag объявлен в flag - так, чтобы ADL мог его найти.)

Весь вопрос заключается в том, как сделать writer связанный класс без знания значения T, чтобы ADL мог его найти?

4b9b3361

Ответ 1

Добавить field_type объявление в шаблон флага с типом возврата auto (доступно только после С++ 14)

Работает только в gcc 4.9:

#include <type_traits>

template<int N>
struct flag {
    friend constexpr int adl_flag (flag<N>);
    friend constexpr auto field_type(flag<N>);
};

template<typename T, int N>
struct writer {
    friend constexpr int adl_flag (flag<N>) { return N; }
    friend constexpr auto field_type(flag<N>) { return (T&&)(*(T*)0); }  // remove default constructable restriction
    static constexpr int value = N;
};

template<int N, class = char[noexcept(adl_flag(flag<N> ()))?+1:-1]>
int constexpr reader (int, flag<N>) { return N; }

template<int N>
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1>())) { return R; }

int constexpr reader (float, flag<0>) { return 0; }

template<typename T, int N = 1, int C = reader (0, flag<32> ())>
int constexpr next (int R = writer<T, C + N>::value) { return R; }

int main () {
    constexpr int a = next<int> ();
    constexpr int b = next<double> ();
    constexpr int c = next<long> ();

    // YES! it works!!!
    static_assert (a == 1 && b == a+1 && c == b+1, "try again");
    static_assert(std::is_same<decltype(field_type(flag<1>{})), int>{}, "first is int");
    static_assert(std::is_same<decltype(field_type(flag<2>{})), double>{}, "second is double");
    static_assert(std::is_same<decltype(field_type(flag<3>{})), long>{}, "third is long");

}

Использовать decltype автофункций вместо noexcept, работает после gcc 5.2, clang 3.5.1 - 3.7.1:

#include <type_traits>

template <int N>
struct flag {
    constexpr friend auto adl_flag(flag<N>);
    friend auto field_type(flag<N>);
};

template<typename T, int N>
struct writer {
    friend constexpr auto adl_flag(flag<N>) { return 0; }
    friend auto field_type(flag<N>) { return (T&&)(*(T*)0); }
    static constexpr int value = N;
};

template<int N, class = decltype(adl_flag(flag<N>{}))>
int constexpr reader (int, flag<N>) { return N; }

template<int N>
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1>())) { return R; }

int constexpr reader (float, flag<0>) { return 0; }

template<typename T, int N = 1, int C = reader (0, flag<32> ())>
int constexpr next (int R = writer<T, C + N>::value) { return R; }

int main () {
    constexpr int a = next<int> ();
    constexpr int b = next<double> ();
    constexpr int c = next<long> ();

    // YES! it works!!!
    static_assert (a == 1 && b == a+1 && c == b+1, "try again");
    static_assert(std::is_same<decltype(field_type(flag<1>{})), int>{}, "first is int");
    static_assert(std::is_same<decltype(field_type(flag<2>{})), double>{}, "second is double");
    static_assert(std::is_same<decltype(field_type(flag<3>{})), long>{}, "third is long");

}