使接受可选的函数接受非可选?
Make a function accepting an optional to accept a non-optional?
我正在尝试在 std::optional
上以 monad 风格编写语法糖。请考虑:
template<class T>
void f(std::optional<T>)
{}
照原样,即使存在转换,也不能使用非可选 T
1(例如 int
)调用此函数从 T
到 std::optional<T>
2.
有没有办法让 f
接受 std::optional<T>
或 T
(在调用方站点转换为可选),而无需定义重载3?
1) f(0)
: error: no matching function for call to 'f(int)'
和 note: template argument deduction/substitution failed
, (demo).
2) 因为模板参数推导不考虑转换。
3) 重载对于一元函数来说是一个可以接受的解决方案,但是当你有像 operator+(optional, optional)
这样的二元函数时,重载就开始成为一种烦恼,并且对于三元、4 元函数来说是一种痛苦, 等函数。
不要将可选参数作为参数,而是将可扣除的模板参数作为参数:
template<class T>
struct is_optional : std::false_type{};
template<class T>
struct is_optional<std::optional<T>> : std::true_type{};
template<class T, class = std::enable_if_t<is_optional<std::decay_t<T>>::value>>
constexpr decltype(auto) to_optional(T &&val){
return std::forward<T>(val);
}
template<class T, class = std::enable_if_t<!is_optional<std::decay_t<T>>::value>>
constexpr std::optional<std::decay_t<T>> to_optional(T &&val){
return { std::forward<T>(val) };
}
template<class T>
void f(T &&t){
auto opt = to_optional(std::forward<T>(t));
}
int main() {
f(1);
f(std::optional<int>(1));
}
这使用了我最喜欢的类型特征之一,它可以根据类型检查任何全类型模板,看看它是否是它的模板。
#include <iostream>
#include <type_traits>
#include <optional>
template<template<class...> class tmpl, typename T>
struct x_is_template_for : public std::false_type {};
template<template<class...> class tmpl, class... Args>
struct x_is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};
template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<x_is_template_for<tmpl, std::decay_t<Ts>>...>;
template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;
template <typename T>
void f(T && t) {
auto optional_t = [&]{
if constexpr (is_template_for_v<std::optional, T>) {
return t;
} else {
return std::optional<std::remove_reference_t<T>>(std::forward<T>(t));
}
}();
(void)optional_t;
}
int main() {
int i = 5;
std::optional<int> oi{5};
f(i);
f(oi);
}
另一个版本。这个不涉及写特征:
template <typename T>
struct make_optional_t {
template <typename U>
auto operator()(U&& u) const {
return std::optional<T>(std::forward<U>(u));
}
};
template <typename T>
struct make_optional_t<std::optional<T>> {
template <typename U>
auto operator()(U&& u) const {
return std::forward<U>(u);
}
};
template <typename T>
inline make_optional_t<std::decay_t<T>> make_optional;
template <typename T>
void f(T&& t){
auto opt = make_optional<T>(std::forward<T>(t));
}
另一个版本。这个不涉及任何东西:
template <typename T>
void f(T&& t) {
std::optional opt = std::forward<T>(t);
}
Class 模板参数推导在这里已经做了正确的事情。如果 t
是一个 optional
,复制扣除候选者将是首选,我们得到相同的类型。否则,我们包装它。
我正在尝试在 std::optional
上以 monad 风格编写语法糖。请考虑:
template<class T>
void f(std::optional<T>)
{}
照原样,即使存在转换,也不能使用非可选 T
1(例如 int
)调用此函数从 T
到 std::optional<T>
2.
有没有办法让 f
接受 std::optional<T>
或 T
(在调用方站点转换为可选),而无需定义重载3?
1) f(0)
: error: no matching function for call to 'f(int)'
和 note: template argument deduction/substitution failed
, (demo).
2) 因为模板参数推导不考虑转换。
3) 重载对于一元函数来说是一个可以接受的解决方案,但是当你有像 operator+(optional, optional)
这样的二元函数时,重载就开始成为一种烦恼,并且对于三元、4 元函数来说是一种痛苦, 等函数。
不要将可选参数作为参数,而是将可扣除的模板参数作为参数:
template<class T>
struct is_optional : std::false_type{};
template<class T>
struct is_optional<std::optional<T>> : std::true_type{};
template<class T, class = std::enable_if_t<is_optional<std::decay_t<T>>::value>>
constexpr decltype(auto) to_optional(T &&val){
return std::forward<T>(val);
}
template<class T, class = std::enable_if_t<!is_optional<std::decay_t<T>>::value>>
constexpr std::optional<std::decay_t<T>> to_optional(T &&val){
return { std::forward<T>(val) };
}
template<class T>
void f(T &&t){
auto opt = to_optional(std::forward<T>(t));
}
int main() {
f(1);
f(std::optional<int>(1));
}
这使用了我最喜欢的类型特征之一,它可以根据类型检查任何全类型模板,看看它是否是它的模板。
#include <iostream>
#include <type_traits>
#include <optional>
template<template<class...> class tmpl, typename T>
struct x_is_template_for : public std::false_type {};
template<template<class...> class tmpl, class... Args>
struct x_is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};
template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<x_is_template_for<tmpl, std::decay_t<Ts>>...>;
template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;
template <typename T>
void f(T && t) {
auto optional_t = [&]{
if constexpr (is_template_for_v<std::optional, T>) {
return t;
} else {
return std::optional<std::remove_reference_t<T>>(std::forward<T>(t));
}
}();
(void)optional_t;
}
int main() {
int i = 5;
std::optional<int> oi{5};
f(i);
f(oi);
}
另一个版本。这个不涉及写特征:
template <typename T>
struct make_optional_t {
template <typename U>
auto operator()(U&& u) const {
return std::optional<T>(std::forward<U>(u));
}
};
template <typename T>
struct make_optional_t<std::optional<T>> {
template <typename U>
auto operator()(U&& u) const {
return std::forward<U>(u);
}
};
template <typename T>
inline make_optional_t<std::decay_t<T>> make_optional;
template <typename T>
void f(T&& t){
auto opt = make_optional<T>(std::forward<T>(t));
}
另一个版本。这个不涉及任何东西:
template <typename T>
void f(T&& t) {
std::optional opt = std::forward<T>(t);
}
Class 模板参数推导在这里已经做了正确的事情。如果 t
是一个 optional
,复制扣除候选者将是首选,我们得到相同的类型。否则,我们包装它。