原文
库
//概念化(需要`C++20`)
struct 可画 {
void 画(小出流 &out) const {
te::call([](auto const &s, auto &out)
-> decltype(s.画(out)) { s.画(out); }, *this, out);
}
};
struct 方形 {
void 画(小出流 &out) const { out << "方形"; }
};
template<te::概念化<可画> T>
void 画(T const &可画) {
可画.画(cout);
}
int main() {
auto 可画 = 方形{};
画(可画); //打印方形
}
首先是利用有状态的模板元绑定鸭子类型
与接口.
struct 可画 {
void 画(小出流 &out) const {
te::call([](auto const &s, auto &out)
-> decltype(s.画(out)) { s.画(out); }, *this, out);
}
};
表面是分发给自己实现的画
函数;但里层却被te
库传递给te::call
的λ
式的类型,绑定了可画
型.
接着分析te::call
的里层实现:
template <...>
constexpr auto call(const TExpr expr, const I &interface, Ts &&... args)
{
...
return detail::call_impl<I>(...);
}//转发
template <...>
constexpr auto call_impl(...)
{
void(类 映射<I, N>::元 set<型列<TExpr, Ts...> >{});
return ...;
}
代码非常简单,展开实例化,绑定了映射
与te::call
传递的λ
型.
绑定是靠友元注入
:
template <class, size_t>
struct 映射 final {
friend auto get(映射);
template <class T>
struct set {
friend auto get(映射) { return T{}; }
};
};
实例化映射
元后,get
友元函数会有个声明式,此时无法确定auto
返回值的类型.试实例化映射
元结果的set
内嵌元时,有get
友元函数的定义式
,此时按T
绑定auto
返回值的类型.
就可
简单地根据能否推导出get
的返回值
类型来决定绑定结果
.以可画
的示例,绑定
结果如下:
映射<可画, 1>::set<型列<λ型, 小出流&>>
概念化
的过程.遍历
所有编译期绑定结果,每个绑定
都记录了鸭子类型
和λ
类型及调用形参组成的型列
.
然后测试
每一个绑定的λ
型,看看是否可正确实例化λ
类型结果的元调用符
.实现代码如下:
template <class T, class TExpr, class... Ts>
constexpr auto requires_impl(型列<TExpr, Ts...>)
-> decltype(&TExpr::template operator()<T, Ts...>);
template <class I, class T, size_t... Ns>
constexpr auto requires_impl(index_sequence<Ns...>) -> 型列<
decltype(requires_impl<I>(decltype(get(映射<T, Ns + 1>{})){}))...>;
} //名字空间细节
template <class I, class T>
concept bool 概念化 = requires {
detail::requires_impl<I, T>(
make_index_sequence<detail::映射大小<T, I>()>{});
};
对可画
鸭子类型与方形
实现类型的约束检查过程如下:
首先方形
对象传入了画
方法,会实例化te::conceptfiy
并检查是否
满足约束:
struct 方形 {
void 画(小出流 &out) const { out << "方形"; }
};
template<te::概念化<可画> T>
void 画(T const &可画) {
可画.画(cout);
}
画(方形{});
检查约束是否满足
时,会遍历可画
上绑定
的所有接口.这里只绑定了画
函数,所以只会遍历一个绑定结果:
auto λ = [](auto const &s, auto &out)
-> decltype(s.画(out)) { s.画(out); };
using λ型 = decltype(λ);
型列<λ型, 小出流&>;
此时试实例化并推导&λ型::元 符号()<小出流&>
的类型,如果成功则满足约束
.注意,该λ
的能否成功推导关键在,λ
函数体后置
返回值式的推导
结果:
[](auto const &s, auto &out)
//关键点:尾返回类型
-> decltype(s.画(out))
//关键点:尾返回类型
{ s.画(out); };
此时s
的类型推到为方形 const&
,如果方形
类型没有实现画
方法,返回值推导无法,整个λ
式的推导
触发会SFINAE
机制,导致实例化失败.
最后输出
编译器错误是不满足概念化
的约束,非常优雅!