从模板化转换函数返回可选值

问题描述 投票:0回答:1

我想更新具有以下签名的转换函数:

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

c++ templates c++17
1个回答
0
投票

有两件事:

首先,您不想更改模板参数的顺序。应该是:

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
的情况,但有些人似乎确实更喜欢它。)

© www.soinside.com 2019 - 2024. All rights reserved.