如何使C++ ADL查看模板的所有实例?

ami*_*min 22 c++ template-meta-programming constexpr c++11 c++14

我正在关注如何在C++教程中实现一个常量表达式计数器我正在尝试修复C++ 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");
}
Run Code Online (Sandbox Code Playgroud)

注意:如果你现在不感兴趣,那么现在是停止阅读的好时机:-)

该演讲解释了如何使用聚合初始化和隐式转换运算符提取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;
};
Run Code Online (Sandbox Code Playgroud)

field_type(flag<N>)将给我类型的N领域.请注意,它是友元函数,对于类型的N第th个字段,编译器将完全定义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>.我怎样才能做到这一点?

更新

正如@TC提到的ADL只查看关联的类,而writer不是一个.(这就是为什么adl_flag声明flag- 所以ADL可以找到它.)

整个问题是如何在writer不知道T值的情况下创建关联类,以便ADL找到它?

use*_*915 3

field_type向标志模板添加声明,并带有返回类型auto(仅在 C++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");

}
Run Code Online (Sandbox Code Playgroud)

使用 auto 函数的 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");

}
Run Code Online (Sandbox Code Playgroud)