Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使接受可选函数的函数接受非可选函数?_C++_Templates_C++17_Optional_Template Argument Deduction - Fatal编程技术网

C++ 使接受可选函数的函数接受非可选函数?

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)和没有匹配函数注意:模板参数

我试图用monad风格在std::optional上编写语法sugar。请考虑:

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)
是否可以编译?