【问题标题】:Use type parameters in return value container types在返回值容器类型中使用类型参数
【发布时间】:2021-03-10 09:11:13
【问题描述】:

我正在尝试创建一组函数,允许它们的调用者将运行时变量值转换为特定于编译时的等效项。就我而言,运行时变量是 Windows IInspectables,转换过程通常称为“拆箱”(尽管出于我的 C++ 问题的目的,这些细节只是说明性的)。我希望转换函数适用于标量(例如 bool、int、double)、容器(例如 optional、pair、tuple、vector)和容器组合(例如 vector>)。容器拆箱函数应使用所包含类型的拆箱函数。

我知道可以通过输出参数的重载解析来表达所需的容器类型,例如

template<typename T1, typename T2>
void Unbox(/* in */ IInspectable* boxedValue, /* out */ std::pair<T1, T2>& unboxedValue)

template<typename T1, typename T2>
void Unbox(/* in */ IInspectable* boxedValue, /* out */ std::tuple<T1, T2>& unboxedValue)

是否可以通过返回值而不是输出参数来做同样的事情?我知道这是不正确/不可用的,但从概念上讲,我想定义……

template<typename T1, typename T2>
std::pair<T1, T2> Unbox(IInspectable* insp);

template<typename T1, typename T2>
std::tuple<T1, T2> Unbox(IInspectable* insp);

... 调用者控制是否创建一对或 2 元素元组。如果可能,实现这一目标的正确模式是什么?如果拆箱函数是(参数化)结构的(静态)方法,这是可以接受的。

【问题讨论】:

  • template&lt;typename... Ts&gt; std::tuple&lt;Ts...&gt; Unbox(IInspectable*); 应该是可能的。如果你想要返回对/元组,问题是消除你的函数的歧义。
  • template&lt;typename T&gt; T Unbox(IInspectable*); 并在函数内部进行调度,调用为Unbox&lt;std::pair&lt;bool, int&gt;&gt;
  • 在您的工作解决方案中,您负责选择您希望boxedValue 的拆箱方式,并通过提供适当类型的第二个参数来进行选择。在您想要的解决方案中,一个简单的问题是:谁/什么负责选择,如果不是您?您仍然需要让函数知道您想要输出什么。你可以通过提供一个模板参数来做到这一点,就像@super的第三个选项一样。我认为没有办法获得您想要的两个同名函数的签名。除非返回类型以某种方式依赖于IInspectable...

标签: c++ template-meta-programming


【解决方案1】:

要么给他们起不同的名字,比如

template<typename T1, typename T2>
std::pair<T1, T2> UnboxPair(IInspectable* insp);

template<typename T1, typename T2>
std::tuple<T1, T2> UnboxTuple(IInspectable* insp);

另一种选择是将完整的返回类型传递给函数

template<typename T>
T Unbox(IInspectable* insp) {
    using T1 = std::tuple_element_t<0, T>; // works on std::pair and std::array as
    using T2 = std::tuple_element_t<1, T>; // well as std::tuple
    ...
}

auto myTuple = Unbox<std::tuple<int, int>>(...);

第三种选择是将包含类型作为模板模板参数传递。

template<template <typename...> typename Container, typename T1, typename T2>
Container<T1, T2> Unbox(IInspectable* insp);

auto myPair = Unbox<std::pair, int, int>(...);

【讨论】:

  • 我喜欢“标签参数”选项:template &lt;typename&gt; struct Tag{}; 然后template &lt;typename T1, template T2&gt; std::pair&lt;T1, T2&gt; Unbox(Tag&lt;std::pair&lt;T1, T2&gt;&gt;, IInspectable*)
  • @super 1/5 感谢您的回复!我将坚持让调用者控制是否创建一对或 2 元素元组作为我在这里询问的基本问题的表示。
  • @super 2/5 回复:“给他们起不同的名字”:这对我来说不是一个真正的选择,因为这是推断转换目标类型的解决方案的一部分。
  • @super 3/5 回复:“将完整的返回类型传递给函数”:对我来说这将是理想的解决方案——我只是不知道该怎么做。假设您建议定义一个模板来创建一对,一个模板来创建一个元组,您如何避免这两个模板发生冲突(在我的例子中,msvc C2995 'function template has been defined')?也许我可以通过 std::enable_if 激活适当的模板,或者通过模板正文中的 'constexpr if' 创建所需的容器?
  • @super 4/5 回复:“模板模板参数”我试过了,但假设这会导致所需容器类型的部分实例化,我无法计算出它的语法(例如,运行到 msvc C2768 '非法使用显式模板参数')。
【解决方案2】:

我喜欢“标签参数”选项:

// Possibly in "private" namespace

// structure for tag dispatching.
template <typename> struct Tag{};

// Overloads of the different Unbox implementations
// Allow custom-point for user classes.
template <typename T1, template T2>
std::pair<T1, T2> Unbox(Tag<std::pair<T1, T2>>, IInspectable*)
{
    // ...
}

template <typename Ts...>
std::tuple<Ts...> Unbox(Tag<std::tuple<Ts...>>, IInspectable*)
{
    // ...
}


float Unbox(Tag<float>, IInspectable*)
{
    // ...
}
// any other overloads, even a fallback for Tag<T>

所以用法是:

auto p = Unbox(Tag<std::pair<bool, float>>{}, inspectable);
auto t = Unbox(Tag<std::tuple<bool, float>>{}, inspectable);

要使用另一种语法,您可以这样做:

template <typename T>
T Unbox(IInspectable* inspectable)
{
    return Unbox(Tag<T>{}, inspectable);
}

所以用法会变成:

auto p = Unbox<std::pair<bool, float>>(inspectable);
auto t = Unbox<std::tuple<bool, float>>(inspectable);
  • 注意:Tag 语法在模板函数中可能更简单,当这些重载是成员时:

    template &lt;typename T&gt; void (T&amp; obj) { obj.Unbox(Tag&lt;int&gt;{}, inspectable); }

    template &lt;typename T&gt; void (T&amp; obj) { obj.template Unbox&lt;int&gt;(inspectable); }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-07
    • 2016-09-16
    • 2021-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多