c++ - جعل وظيفة قبول اختياري لقبول غير اختياري؟




templates c++17 (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::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 ، ( demo ).
2) لأن خصم وسيطة القالب لا يأخذ في الاعتبار التحويلات.
3) يعتبر التحميل الزائد حلاً مقبولًا لوظيفة أحادية ، ولكنه يبدأ أن يكون مصدر إزعاج عندما يكون لديك وظائف ثنائية مثل operator+(optional, optional) ، ويشكل ألمًا للوظائف الثلاثية ، 4-ary ، إلخ .


نسخة أخرى. هذا واحد لا ينطوي على أي شيء:

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

قالب حجة الطبقة خصم بالفعل يفعل الشيء الصحيح هنا. إذا كانت t optional ، فسيكون مرشح خصم النسخ المفضل ونحصل على نفس النوع مرة أخرى. خلاف ذلك ، نحن لفه.


يستخدم هذا أحد سمات الكتابة المفضلة الخاصة بي ، والتي يمكنها التحقق من أي قالب all-type مقابل نوع لمعرفة ما إذا كان هو القالب الخاص به.

#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







template-deduction