f | transform([](auto value) { return value; }) == f
f | transform(t1) | transform(t2) ==
f | transform([=](auto value) { return t2(t1(value)); })
std::transform
和 view::transform
类似
std::optional
类型是一个基本的仿函数std::optional
定义转换函数示例template <typename T1, typename F>
auto transform(const std::optional<T1> &opt, F f)
-> decltype(std::make_optional(f(opt.value())))
{
if (opt) {
return std::make_optional(f(opt.value()));
} else {
return {};
}
}
std::string user_full_name(const std::string& login)
{
return "full_name";
}
std::string to_html(const std::string& text)
{
return "html";
}
std::string current_login = "login";
auto html = transform(transform(current_login, user_full_name), to_html);
std::optional
定义 range 示例
join(transform(
join(transform(
login,
user_full_name)),
to_html));
auto login_as_range = as_range(current_login);
login_as_range | ranges::v3::view::transform_fn(user_full_name)
| ranges::v3::view::join_fn
| ranges::v3::view::transform_fn(to_html)
| ranges::v3::view::join_fn;
construct : T -> M
mbind: (M, T1 -> M) -> M
mbind(m, construct) == m
mbind(mbind(m, f), g) == mbind(m, [](auto x){ return mbind(f(x), g) })
std::vector
构造一个仿函数
//把给定的向量看作 range,对其中每个元素调用f进行转换
//就像仿函数要求的一样,函数 f 返回一个向量
template <typename T, typename F>
auto transform(const std::vector<T> xs, F f)
{
return xs | ranges::v3::view::transform_fn(T)
| ranges::v3::to_vector;
}
template <typename T>
std::vector<T> make_vector(T&& value)
{
return { std::forward<T>(value) };
}
template <typename T, typename F>
//f 接收一个T类型的值,返回 T 类型或其他类型的vector
auto mbind(const std::vector<T>& xs, F f)
{
//调用 f 产生一个向量类型的 range, 可以把它转换成向量的向量
auto transformed =
xs | ranges::v3::view::transform_fn(f)
| ranges::v3::to_vector;
//所需要的不是向量的向量,而是所有值在一个向量中
return transformed
| ranges::v3::view::join_fn
| ranges::v3::to_vector;
}
template <typename C, typename P>
auto filter(const C& collection, P predicate)
{ //依据当前元素是否满足谓词,接收0个或1个元素
return collection
| mbind([=](auto element) {
return ranges::v3::view::single_fn(element)
| ranges::v3::view::take_fn( predicate(element) ? 1 : 0);
});
}
//指定返回类型,如果没有值则返回{}
template <typename T, typename F>
auto mbind(const std::optional<T>& opt, F f) -> decltype(f(opt.value()))
{
if (opt)
{
//如果包含一个值,则调用 f 对值进行转换
//并返回转换结果
return f(opt.value());
}
else
{
return {};
}
}
std::optional<std::string> current_user_html()
{
return mbind(
mbind(current_login, user_full_name),
to_html
);
}
std::optional<std::string> current_user_html()
{
return current_login | mbind(user_full_name)
| mbind(to_html);
);
}
template <
typename T, typename E, typename F,
//f 可能返回不同类型,因此在返回之前,需要类型推断
typename Ret = std::invoke_result<F(T)>::type
>
Ret mbind(const expected<T, E>& exp, F f)
{
if (!exp)
{
//如果 exp 包含错误,则继续把它传递下去
return Ret::error(exp.error());
}
return f(exp.value());
}
expected<std::_Invoker_strategy, int> cuurent_user_html()
{
return current_login | mbind(user_full_name)
| mbind(to_html);
}
template <
typename F,
typename Ret = std::invoke_result<F()>::type,
typename Exp = expected<Ret, std::exception_ptr>
>
//函数 f 没有参数,如果要使用参数调用它,可以传递lambda表达式
Exp mtry(F f)
{
try
{
//如果没有抛出异常,则返回一个 expected 示例
//包含 f 的返回结果
return (Exp::success(f());
}
catch (...)
{
//如果有异常抛出,则返回一个 expected 实例
//包含指向该异常的指针
return Exp::error(std::current_exception());
}
}
auto result = mtry([=] {
auto users = system.users();
if (user.empty())
{
throw std::runtime_error("No users");
}
return users[0];
});
template <typename T>
T get_or_throw(const expected<T, std::exception_ptr>& exp)
{
if (exp)
{
return exp.value();
}
else
{
std::rethrow_exception(exp.error());
}
}
template <typename T>
class with_log
{
public:
with_log(T value, std::string log = std::string())
: m_value(value)
, m_log(log)
{}
T value() const
{
return m_value;
}
std::string log() const
{
return m_log;
}
private:
std::string m_log;
T m_value;
}
template <
typename T,
typename F,
typename Ret = std::invoke_result<F()>::type
>
Ret mbind(const with_log<T1>& val, F f)
{
//使用 f 进行转换,返回转换结果和 f 的日志字符串
const auto result_with_log = f(val.value());
//返回处理结果,但日志不仅仅是 f 的日志,还要与原来的日志进行拼接
return Rec(result_with_log.value(), val.log() + result_with_log.log());
}
future<std::string> current_user_html()
{
return current_user() | mbind(user_full_name)
| mbind(to_html);
}
std::future
与 expected
分 future 类似
std::future
不能智能地附加延续函数then()
(std::experimental::future
)then()
与 mbind 类似,但有不同之处
then()
接收的函数要求其参数为一个完成的 future 对象,并返回一个新的 futurethen()
并不是使 future 变为 monad, 而是使实现 mbind 变得简单template <typename T, typename F>
//接收一个函数 f,它可以把类型 T 转换成 future 的实例 future
auto mbind(const std::experimental::future<T>& future, F f)
{
//接收一个把 future 转换成 future的函数
//在把它传递给函数f之前,需要用lambda表达式提取future中的值
return future.then(
[](std::experimental::future<T> finished) {
//不会阻塞任何程序,因为延续函数只有在结果准备好时(或有异常时)
//才会被调用
return f(finished.get());
}
);
}
user_full_name: std::string -> M
to_html: std::string -> M
M
代替了 optional
、expected
或其他包装类型M<std::string> user_htlm(const m<std::string>& login)
{
return mbind(
mbind(login, user_full_name),
to_html
);
}
f: T1->T2
和 g: T2->T3
f: T1->M
和 g: T2->M
template <typename F, typename G>
auto mcompose(F f, G g)
{
return [=](auto value) {
return mbind(f(value), g);
};
}
mcompose()
auto user_html = mcompose(user_full_name, to_html);
mcompose()
函数可以编写更简短、更通用的代码mcompose(f, construct) == f
mcompose(construct, f) == f
mcompose(f, mcompose(g, h)) == mcompose(mcompose(f, g), h)
template <typename T>
using contained_type_t = decltype(*begin(T()));
contained_type_t
就是一个元函数
std::declval()
工具代替构造函数调用
decltype
template <typename C,
typename R = contained_type_t<C>>
R sum(const C& collection)
{
...
}
contained_type_t
第二个问题
contained_type_t
推断的类型template <typename T>
class error {};
error<contained_type_t<std::vector<std::string>>>();
contained_type_t
推断类型是字符串的常引用类型,而不是用户想要的字符串类型contained_type_t
std::remove_cv_t
元函数std::remove_reference_t
元函数template <typename T>
using contained_type_t =
std::remove_cv_t<
std::remove_reference_t<
decltype(*begin(std::declval<T>()))
>
>;
_t
结尾的元函数在 C++14 中引入static_assert
static_assert(
std::is_same<int, contained_type_t<std::vector<int>>>(),
"std::vector should contain integers"
);
std::is_same
is_same
元函数
std::true_type
或 std::false_type
is_same
有两种情况
std::false_type
std::true_type
template <typename T1, typename T2>
struct is_same : std::false_type{};
std::false_type
template <typename T>
struct is_same<T, T> : std::true_type{};
is_same>>
的 is_same 的定义,并选择最具体的那个is_same
元函数实现的理解
//通常情况下,remove_reference::type 的类型应该是T
//它接收什么类型就返回什么类型
template <typename T>
struct remove_reference {
using type = T;
};
//如果接收左值引用T&,剥离引用返回T
template <typename T>
struct remove_reference <T&>{
using type = T;
};
//如果接收右值引用T&&,剥离引用返回T
template <typename T>
struct remove_reference <T&&> {
using type = T;
};
contained_type_t
使用模板类型别名实现的remove_reference
元函数获取结果类型就必须创建该类型的模板,并获得嵌套在其中的类型定义
typename remove_reference::type
template <class T>
using remove_reference_t = typename remove_reference<T>::type;
template <typename T, typename E>
class expected
{
public:
using value_tyoe = T;
};
template<typename ...>
using void_t = void;
void_t
value_type
value_type
的元函数//通常情况:假设任意类型都没有内嵌 value_type 类型定义
template <typename C, typename = std::void_t<>>
struct has_value_type : std::false_type {};
// 特殊情况,只考虑C::value类型为已存在类型
//(如果C包含内嵌的value_type类型)
template <typename C>
struct has_value_type<C, std::void_t<typename C::value_type>> : std::true_type {};
value_type
类型的集合has_value_type
判断使用哪个方法template <typename C>
auto sum(const C& collection)
{
if constexpr (has_value_type(collection))
{
return sum_collection(collection);
}
else
{
return sum_iterable(collection);
}
}
constexpr-if
constexpr-if
要求两个分支都必须为有效的语法,但不会编译两个分支void_t
不但可以检查类型的有效性,还可以通过 decltype 和 std::declval的帮助对表达式进行检查//通常情况:假设任何类型都不可迭代
template <typename C, typename = std::void_t<>>
struct is_iterable : std::false_type {};
//特殊情况:仅考虑C可迭代且其begin迭代器可解引用
template <typename C>
struct is_iterable<
C,
std::void_t<
decltype(*begin(std::declval<C>())),
decltype(end(std::declval<C>()))
>
>
: std::true_type {};
template <typename C>
auto sum(const C& collection)
{
if constexpr (has_value_type(collection))
{
return sum_collection(collection);
}
else if constexpr (is_iterable<C>)
{
return sum_iterable(collection);
}
else
{
}
}
std::tuple
中存储所有捕获的参数
std::decay_t
保证类型参数不是引用而是实际的值std::is_invocable_v
元函数
std::invoke
std::invoke
std::apply
std::invoke
相似with(martha) (name = "Martha", surname = "Jones", age = 42);
std::is_invocable
std::is_invocable_r