我想要一個只有 1 個引數的函式,該引數對于泛型型別是可選的,并已分配boost::none為默認值。那可能嗎?
#include <iostream>
#include <string>
#include <boost/optional.hpp>
template <typename T>
void f(boost::optional<T> v = boost::none)
{
if (v)
{
std::cout<<"v has value: " << v.get();
}
else
{
std::cout<<"v has no value!";
}
}
int main()
{
f(12);
f("string");
return 0;
}
uj5u.com熱心網友回復:
是的,這是可能的,但它不能與模板推導一起使用(f(12)將嘗試實體化 a f<int&&>,它不存在)。當您這樣呼叫它時,您的代碼將編譯:
f(boost::optional<int>{12});
或顯式實體化它:
f<int>(12);
uj5u.com熱心網友回復:
嗯,另一個答案很接近。但不完全在那里。f(12)不會“嘗試實體化f<int&&>”。事實上,它無法推匯出 T,因為 T 處于非推導背景關系中。
另外,您的問題是題外話:即使沒有默認值,您也會遇到同樣的問題:Compiler Explorer
template <typename T> void f(boost::optional<T> v) {
if (v) {
std::cout<<"value: " << v.get() << "\n";
} else {
std::cout<<"no value\n";
}
}
int main()
{
f(12);
f("string");
}
現在,在我盲目地向您展示如何解決所有這些問題之前,請先問自己一個問題:我們在這里做什么。
如果您想要默認引數,根據定義,這是否意味著它們不是可選值?也許您只需要:Compiler Explorer
template <typename T> void f(T const& v) {
std::cout << "value: " << v << "\n";
}
void f() {
std::cout << "no value\n";
}
int main()
{
f(12);
f("string");
f();
}
印刷
value: 12
value: string
no value
使用一些hackery,您可以通過默認模板型別引數來組合多載:
template <typename T = struct not_given*> void f(T const& v = {}) {
if constexpr(std::is_same_v<T, not_given*>) {
std::cout << "no argument\n";
} else {
std::cout << "value: " << v << "\n";
}
}
列印編譯器資源管理器
value: 12
value: string
no argument
如果你需要怎么辦optional<>
In that case, in you specific example you would probably want optional<T const&> to avoid needlessly copying all the arguments; but see std::optional specialization for reference types.
If You Really Really Want1
Say, you MUST have the semantics you were looking for. You do not care that you won't be able to know the difference between calling with no argument vs. calling with an uninitialized optional (none). This is kinda like many scripting languages, right?
Now you have to make the template argument become deduced context, and then want to ensure that... it is an optional<T>:
template <typename T, typename = void> struct is_optional : std::false_type { };
template <typename T> struct is_optional<boost::optional<T>> : std::true_type { };
template <typename T = boost::optional<void*> >
std::enable_if_t<is_optional<T>::value> f(T const& v = {}) {
if (v) {
std::cout << "value: " << *v << "\n";
} else {
std::cout << "no value\n";
}
}
template <typename T>
std::enable_if_t<not is_optional<T>::value> f(T const& v) {
return f(boost::make_optional(v));
}
int main()
{
f(12);
f("string");
f();
}
One "advantage" is that that now you clearly see the copying being done.
Another "advantage" is that now you can support std::optional the same way: https://godbolt.org/z/1Mhja83Wo
template <typename T> struct is_optional<std::optional<T>> : std::true_type { };
Summary
I hope this answer gets the point across that C is not a dynamically typed language. This implies that the idea of optional arguments of "unknown" type is really not idiomatic. (It might be a bit unfortunate that Boost called it boost::none instead of e.g. std::nullopt, perhaps giving people associations with Python's None.)
Instead, you can use static polymorphism. The simplest version of that was the first I showed, using function overloading.
If you were to mimic a dynamic type interface in C , you would probably use std::variant or std::any instead. To restrict the bound types you would use concepts (this is getting a bit deep, but see e.g. Boost Type Erasure).
1 i really really really wanna zig a zig ah
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/433970.html
