web-dev-qa-db-ja.com

非オプションを受け入れるためにオプションを受け入れる関数を作成しますか?

_std::optional_を介して、モナドスタイルで構文シュガーを記述しようとしています。考えてください:

_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_、( デモ )。
2) テンプレート引数の推論は変換を考慮しないためです。
3) オーバーロードは単項関数の許容可能な解決策ですが、operator+(optional, optional)のようなバイナリ関数がある場合は厄介になり始め、3項、4項、などの苦痛です。 機能。

46
YSC

別のバージョン。これには何も関係しません:

template <typename T>
void f(T&& t) {
    std::optional opt = std::forward<T>(t);
}

ここでは、クラステンプレート引数の演がすでに正しいことを行っています。 toptionalの場合、コピー控除候補が優先され、同じタイプが返されます。それ以外の場合は、ラップします。

37
Barry

引数としてオプションを使用する代わりに、控除可能なテンプレートパラメータを使用します。

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));
}

実例

15
bartop

これは、私のお気に入りの型特性の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);
}

https://godbolt.org/z/HXgoEE

13
xaxxon

別のバージョン。これには、特性の記述は含まれません。

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));
}
5
Barry