我正在关注如何在 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");
}
注意:如果您现在不感兴趣,现在是停止阅读的好时机:-)
演讲解释了如何使用聚合初始化和隐式转换运算符提取 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. 提到的,ADL 仅查看关联的类,而
writer
不是其中之一。 (这就是为什么 adl_flag
在 flag
中声明 - 以便 ADL
可以找到它。)
如何在不知道
writer
值的情况下使 T
成为关联类,以便 ADL
可以找到它?
在标志模板中添加
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");
}
使用 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");
}