【问题标题】:Function template argument deduction failed using std::optional使用 std::optional 推导函数模板参数失败
【发布时间】:2018-03-08 04:57:13
【问题描述】:

我想使用std::optional 编写一个带有可选参数的模板函数,

template<typename T1, typename T2>
void fcn(T1 v1, std::optional<T2> v2)
{
}

int main()
{
    fcn(1, 1);            // failed to compile
    fcn(1, std::nullopt); // failed as well
}

但是,编译器无法针对这两种情况推导出 T2

我知道,当 T1T2 相同时,将第二个参数放入不可演绎的上下文中,例如,

template<typename T>
void fcn(T v1,
         std::enable_if<true, T>::type v2) 
{
}

让调用者可以正常调用fcn(1, 1)。我的第一个问题是如何在不强制调用者写fcn(v1, std::make_optional(v2)) 的情况下为我之前的示例实现这一点?

我的第二个问题是如何使用std::nullopt 调用fcn,而不是编写函数重载?由于在我的用例中可能有几个可选参数,并且编写所有重载排列太不切实际了。

----编辑----

正如 cmets 所建议的,有多种方法可以编译代码,但没有一种方法比普通函数调用更直观。也许还有一个问题要问,编写模板函数接受不同类型的可选参数的正确方法是什么?

【问题讨论】:

  • 就像一个注释,这迫使调用者写fcn&lt;int, int&gt;(1, 1)fcn&lt;int, int&gt;(1, std::nullopt),这似乎比fcn(v1, std::make_optional(v2))更合理。
  • @YSC 但我认为让调用者提供第二个类型参数仍然不是很直观,尽管她打算省略它。
  • @unclejimbo:如果您对 T2 类型的知识毫无用处 - 这样您同样乐意省略参数 - 那么您可以为 @987654335 提供默认值您知道的 @ 参数可以让您的代码编译。但是,这并不能很好地扩展:您只能在没有以后要指定的参数时省略参数,除非您使用一些hackery来模拟命名参数(如果有兴趣,请谷歌)。如果您对类型感兴趣,调用者必须指定它(如我的回答)。
  • 您希望fcn(1, std::nullopt); 调用哪个函数?任何T2 都是有效的(只要它对optional 有效)

标签: c++ templates optional generic-programming


【解决方案1】:

如何在不强制调用者编写 fcn(v1, std::make_optional(v2)) 的情况下为我之前的示例实现这一点?

您可以通过将扣除部分与std::optional的使用分开来做到这一点。

除了编写函数重载之外,如何使用 std::nullopt 调用 fcn?

您可以添加一个模板化重载,它将处理所有std::nullopt 情况。对于这个,您需要指定该可选的模板参数。这不是问题,因为您可以更改2个模板参数的顺序。

template<typename T2, typename T1>
void fcn_impl(T1 v1, std::optional<T2> v2) {}

template<typename T2, typename T1>
void fcn(T1&& v1, T2&& v2)
{
    fcn_impl(std::forward<T1>(v1),
            std::make_optional(std::forward<T2>(v2)));
}

template<class T2, class T1>
void fcn(T1&& v1)
{
    fcn_impl<T2>(std::forward<T1>(v1), std::nullopt);
}

int main()
{
    // call the first one
    fcn(1, 1);

    // call std::nullopt one
    fcn<short>(1); 
}

【讨论】:

    【解决方案2】:

    我的第一个问题是如何在不强制调用者编写 fcn(v1, std::make_optional(v2)) 的情况下为我之前的示例实现这一点

    您可能会使用额外的间接方式(如 liliscent 所示),但对于 std::null_opt,您必须指定类型...

    正如 cmets 所建议的,有多种方法可以编译代码,但没有一种方法比普通函数调用更直观。也许还有一个问题要问,编写模板函数接受不同类型的可选参数的正确方法是什么?

    一种简单的方法是重载:

    template<typename T1, typename T2>
    void fcn(T1 v1, T2 v2)
    {
    }
    
    template<typename T1>
    void fcn(T1 v1)
    {
    }
    

    另一种方法是检查给定类型的函数内部

    template<typename T1, typename T2, typename T3>
    void fcn(T1 v1, T2 v2, T3 v3)
    {
        if constexpr (std::is_same<std::nullopt_t, T2>::value) {
        // ...
        }
        if constexpr (std::is_same<std::nullopt_t, T3>::value) {
        // ...
        }
    }
    

    更复杂的方法是可变参数模板:

    template<typename T1, typename ... Ts>
    void fcn(T1 v1, Ts&&... args)
    {
        if constexpr (sizeof...(Ts) == 0) {
            // ...
        } else /*if constexpr (sizeof...(Ts) == 1)*/ {
            auto& v2 = std::get<0>(std::tie(args));
            // ...
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-10
      • 1970-01-01
      • 2018-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多