我想更新具有以下签名的转换函数:
template<typename In, typename Out>
bool Convert(const In& p_in, Out& p_out);
// Returns `true` if the conversion succeeded, `false` otherwise.
这样我就可以使用
std::optional
来代替。类似于(注意模板参数已切换):
template<typename Out, typename In>
std::optional<Out> Convert(const In& p_in);
就模板而言,有一个很大的变化:模板化类型之一现在用于
Convert
的返回类型。因此,除非指定函数参数,否则编译会失败。示例:
#include <optional>
class A
{
public:
A() = default;
A(int p_value)
: m_value(p_value)
{}
private:
int m_value = 0;
};
template<typename In, typename Out>
bool Convert(const In& p_in, Out& p_out)
{
return false;
}
template<>
bool Convert(const int& p_in, A& p_out)
{
p_out = A(p_in);
return true;
}
template<typename Out, typename In>
std::optional<Out> Convert2(const In& p_in)
{
return std::nullopt;
}
template<>
std::optional<A> Convert2(const int& p_in)
{
return A(p_in);
}
int main()
{
const int in = 2;
// Fine, but not ideal since we have `std::optional` available:
A out;
const bool result1 = Convert(in, out);
// Failed:
//const std::optional<A> result2 = Convert2(in);
// Ok, but sadly A is repeated at called site:
const std::optional<A> result3 = Convert2<A>(in);
return 0;
}
如果我取消第二个调用的注释,这是错误消息(
gcc
):
<source>: In function 'int main()':
<source>:62:46: error: no matching function for call to 'Convert2(const int&)'
62 | const std::optional<A> result2 = Convert2(in);
| ~~~~~~~~^~~~
<source>:30:20: note: candidate: 'template<class Out, class In> std::optional<_Tp> Convert2(const In&)'
30 | std::optional<Out> Convert2(const In& p_in)
| ^~~~~~~~
<source>:30:20: note: template argument deduction/substitution failed:
<source>:62:46: note: couldn't deduce template parameter 'Out'
62 | const std::optional<A> result2 = Convert2(in);
| ~~~~~~~~^~~~
使用
auto
,我能够让它发挥作用:
template<typename In>
auto Convert(const In& p_in)
{
return std::nullopt;
}
template<>
auto Convert(const int& p_in)
{
return std::make_optional<A>(p_in);
}
这的优点是可以在呼叫站点按照我想要的方式使用:
const std::optional<A> result4 = Convert3(in)
但我觉得函数签名现在关于函数功能的信息要少得多。必须阅读实现才能知道返回
std::optional<A>
。
是否有一种方法可以实现此目的,而无需在调用站点指定模板化类型?
template<typename Out, typename In>
std::optional<Out> Convert(const In& p_in)
{
// ?
}
template<>
std::optional<A> Convert(const int& p_in)
{
// ?
}
int main()
{
const std::optional<A> result = Convert(in)
return 0;
}
Godbolt 来源:https://godbolt.org/z/bYjrcPsPx。
有两件事:
首先,您不想更改模板参数的顺序。应该是:
template <typename In, typename Out>
std::optional<Out> convert( In input );
这至少允许对输入类型进行模板类型推导。
其次,在调用该函数时,您必须显式指定
Out
类型,因为没有任何东西可以让编译器推断它。所以:
X out = convert<X>( in );
编译器在推导模板参数时(通常)不使用返回值。
实际上可以让编译器推导
Out
类型,但不值得增加复杂性。编译器会考虑转换的目标,因此实际的转换函数将类似于:
template <typename In>
Helper<In> convert( In value );
Helper
类将包含许多转换运算符:
template <typename In>
struct Helper
{
template <typename Out>
operator std::optional<Out>() { /* actual conversion */ }
};
但是,一般来说,为多种类型实现此功能会非常痛苦,而且一般来说,我认为这不会让用户的生活变得更轻松:你不能写这样的东西:
auto tmp = convert( anX );
if ( tmp.has_value() ) {
Y value = tmp.value();
// ....
}
(这可能是滥用
auto
的情况,但有些人似乎确实更喜欢它。)