【问题标题】:specialization of variadic template function for std::tuple as return type将 std::tuple 的可变参数模板函数特化为返回类型
【发布时间】:2022-01-13 12:12:51
【问题描述】:

我正在实现一个模板函数,它解析一个字符串并返回一个对象元组,比如

template <typename... Objects>
std::tuple<Objects...> convert2data(const std::string_view& input) {
    return std::tuple<Objects...>{ internal::popNext<Objects>(input)... } ;
}

// usage
auto data = convert2data<int, double>("1, 0.01") ;

我将预期类型指定为模板参数。现在我想从命名的元组类型中“直接”获取类型,这对用户来说可能更方便:

using MyData = std::tuple<int, double> ; // included from some header
auto data = convert2data<MyData>("1, 0.01") ;
std::cerr << std::get<int>(data) ;
// or better
MyData data2 = convert2data("1, 0.01") ;

我一直在尝试为这种情况指定专业化,例如这段代码

template <typename... Objects, class std::tuple<Objects...>>
std::tuple<Objects...> convert2data(const std::string_view& input) {
    return convert2data<Objects...>(input) ;
}

没有被编译。有没有办法为返回值中使用模板类型的模板函数定义这种特化?

【问题讨论】:

  • 我看不出convert2data("1, 0.01") 是如何工作的。您似乎期望返回类型(必须在编译时知道)以某种方式从字符串的内容(仅在运行时知道)中神奇地派生出来。
  • 我天真地认为MyData data(convert2data("1, 0.1") ) 会给编译器所需的类型信息,因为它知道构造函数所需的类型。
  • std::tuple 有模板化的构造函数或多或少的任何东西(参见 (3) 和 (4) here)。但即使它没有 - 函数模板参数通常是从函数调用参数推导出来的,而不是从它的返回类型推导出来的(有例外 - 从顶部开始,转换运算符可能会从它被要求转换为的类型推导出来;和也许当一个函数被强制转换或分配给一个函数指针时,推论可能同时适用于该指针的参数类型和返回类型,我不太记得了)。

标签: c++ templates tuples variadic-templates specialization


【解决方案1】:

大概是这样的:

template <typename Tuple, size_t... Is>
Tuple convert2dataHelper(const std::string_view& input,
                         std::index_sequence<Is...>) {
  return std::make_tuple(
      internal::popNext<std::tuple_element_t<Is, Tuple>>(input)...);
}

template <typename Tuple>
Tuple convert2data(const std::string_view& input) {
  return convert2dataHelper<Tuple>(
      input, std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}

这允许convert2data&lt;std::tuple&lt;int, double&gt;&gt;("1, 0.01"); 语法。


另一种基于部分特化的方法,允许同时调用 convert2data&lt;int, double&gt;(...)convert2data&lt;std::tuple&lt;int, double&gt;&gt;(...) 并执行相同的操作。

template <typename... Ts>
struct Convert2dataHelper {
  static std::tuple<Ts...> Do(const std::string_view& input) {
    return std::make_tuple(internal::popNext<Ts>(input)...);
  }
};

template <typename... Ts>
struct Convert2dataHelper<std::tuple<Ts...>> {
  static std::tuple<Ts...> Do(const std::string_view& input) {
    return Convert2dataHelper<Ts...>::Do(input);
  }
};

template <typename... Ts>
auto convert2data(const std::string_view& input) {
  return Convert2dataHelper<Ts...>::Do(input);
}

【讨论】:

  • 谢谢,似乎几乎可以工作,虽然它无法编译,因为它无法在找到 convert2data&lt;std::tuple...&gt; 的 2 个重载时解决歧义,看起来 enable_if 需要在基本的函数模板参数中应用类型和元组...
  • 我没有意识到您希望两种口味同时起作用。我添加了另一种可能的方法来做到这一点。
  • 结构解决方案效果很好,谢谢!我会很感激一个简短的解释为什么需要在模板结构中“包装”函数......这似乎也是从convert2data返回'auto'的关键点,在那里有std :: tuple不起作用......
  • 类可以部分特化,但函数不能。至于返回类型——当Ts... 实际上是tuple&lt;Us...&gt; 的单一类型时,专业化的全部意义在于做一些不同的事情。在这种情况下,您希望返回类型为 tuple&lt;Us...&gt; 而不是 tuple&lt;tuple&lt;Us...&gt;&gt;(这是 tuple&lt;Ts...&gt; 会变成的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多