tutorial smart remix español curso aprender c++ templates c++17 optional template-deduction

c++ - smart - ¿Hacer una función aceptando un opcional para aceptar un no opcional?



solidity español (4)

Estoy tratando de escribir azúcar sintáctica, en un estilo de mónada, más de std::optional . Por favor considera:

template<class T> void f(std::optional<T>) {}

Como es, esta función no puede invocarse con un T no opcional (por ejemplo, un int ), aunque exista una conversión de T a std::optional<T> 2 .

¿Hay alguna manera de hacer que f acepte una std::optional<T> o una T (convertida en una opción en el sitio de la persona que llama), sin definir una sobrecarga 3 ?

1) f(0) : error: no matching function for call to ''f(int)'' y note: template argument deduction/substitution failed , ( demo ).
2) Porque la deducción del argumento de la plantilla no considera las conversiones.
3) La sobrecarga es una solución aceptable para una función unaria, pero comienza a ser una molestia cuando tienes funciones binarias como operator+(optional, optional) , y es un dolor para funciones ternarias, 4-arias, etc.


En lugar de tomar opcional como argumento tomar el parámetro de plantilla deducible:

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

Ejemplo en vivo


Esto utiliza uno de mis rasgos de tipo favoritos, que puede verificar cualquier plantilla de tipo todo contra un tipo para ver si es la plantilla para ello.

#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


Otra version. Este no implica nada:

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

La deducción del argumento de la plantilla de clase ya hace lo correcto aquí. Si t es optional , se preferirá el candidato de deducción de copia y devolveremos el mismo tipo. De lo contrario, lo envolvemos.


Otra version. Esto no implica rasgos de escritura:

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