C++ 使接受可选函数的函数接受非可选函数?
我试图用monad风格在std::optional上编写语法sugar。请考虑:C++ 使接受可选函数的函数接受非可选函数?,c++,templates,c++17,optional,template-argument-deduction,C++,Templates,C++17,Optional,Template Argument Deduction,我试图用monad风格在std::optional上编写语法sugar。请考虑: template<class T> void f(std::optional<T>) {} 模板 空f(标准::可选) {} 实际上,不能使用非可选的T1(例如int)调用此函数,即使存在从T到std::optional2的转换 是否有办法使f接受std::optional或T(在调用方站点转换为可选),而不定义重载 1) f(0):错误:调用'f(int)和没有匹配函数注意:模板参数
template<class T>
void f(std::optional<T>)
{}
模板
空f(标准::可选)
{}
实际上,不能使用非可选的T
1(例如int
)调用此函数,即使存在从T
到std::optional
2的转换
是否有办法使f
接受std::optional
或T
(在调用方站点转换为可选),而不定义重载
1)
f(0)
:错误:调用'f(int)
和没有匹配函数注意:模板参数推断/替换失败,
,()。2)因为模板参数推导不考虑转换。
3) 重载对于一元函数来说是一个可以接受的解决方案,但是当您使用诸如
运算符+(可选,可选)
之类的二元函数时,重载就开始成为一个麻烦,对于三元函数、四元函数等函数来说,重载是一个难题。不是将可选作为参数,而是将可选作为免赔模板参数:
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));
}
模板
结构是可选的:std::false\u类型{};
模板
结构是可选的:std::true\u类型{};
模板
constexpr decltype(自动)到_可选(T&&val){
返回标准::转发(val);
}
模板::值>>
constexpr std::可选到_可选(T&&val){
返回{std::forward(val)};
}
模板
无效f(T&T){
自动选择=to_可选(标准:向前(t));
}
int main(){
f(1);
f(标准::可选(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);
}
#包括
#包括
#包括
模板
结构x_是公共std::false_类型{}的模板;
模板
struct x_是另一个版本的模板。这一条不涉及写作特点:
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));
}
模板
结构使\u可选\u t{
模板
自动运算符()(U&&U)常量{
返回标准::可选(标准::转发(u));
}
};
模板
结构使\u可选\u t{
模板
自动运算符()(U&&U)常量{
返回标准::向前(u);
}
};
模板
内联make_可选\u t make_可选;
模板
无效f(T&T){
自动选择=使_可选(std::forward(t));
}
另一个版本。这一次不涉及任何事情:
template <typename T>
void f(T&& t) {
std::optional opt = std::forward<T>(t);
}
模板
无效f(T&T){
std::可选opt=std::forward(t);
}
类模板参数推断在这里已经做了正确的事情。如果t
是可选的
,则优先选择复制扣减候选项,我们将获得相同的类型。否则,我们就把它包装起来。让它取一个T,然后检查它是否是可选的,如果不是,就做一个。如果你使用c++17,constexpr if可以非常容易地做到这一点。@xaxxon这是值得回答的问题。你能澄清一下你所说的“单子风格”是什么意思吗?因为就目前而言,你的问题表明你对某种自动转换或重载感兴趣,而这两个答案都是沿着这些思路做的。但这与单子没有任何关系。@KonradRudolph提到单子只是为了提供一个上下文。我想要,如果提供F=λxy.
,一个可选(x)
和一个可选(y)
,返回一个可选(F(xy))
。为了让这样一个定义有用并让其用户链接表达式,它需要接受可选和标量。这个定义的问题是,与单子不同,它不组合。您的函数如何处理std::optional
?然而,如果你有一个单子提升操作符,你只需提升(F)
,就不会有这个问题。你在这里得到了非常好的特性。你知道为什么std::decay
是必要的吗?@YSC如果没有std::decay
它就不能自动处理std::optional&
和相关的类型七虽然这不是代码高尔夫,但我觉得这个答案值得打勾:DGood思路;然而,opt在这里不需要定义为std::optional吗?@lthod非常不需要。@Barry--a-ha,我现在明白你的意思了--我以前从未碰到过类模板参数推导!这缺乏过载分辨率特性;有没有办法测试std::optional=std::forward(t)
是否可以编译?