参数和返回类型中的模板类型推导

Posted

技术标签:

【中文标题】参数和返回类型中的模板类型推导【英文标题】:Template type deduction in parameter and return type 【发布时间】:2021-01-26 11:55:17 【问题描述】:

在我们当前的代码库中,我们的代码如下所示(这当然是简化版):

#include <map>
#include <string>
#include <iostream>

using namespace std;

// General template function to convert an enum to a string, given a converter-container
// The converter-container is either a map, or a vector/array etc. of pair, 
// e.g. something that has enum in .first and string in .second

template<typename Container, typename Index>
string toString(Container&& names, Index&& index, const string& noMatch = "NoMatch")

    for (auto&& elem : names) 
      if (elem.first == index) 
         return elem.second;
      
   
   return noMatch;



// Simple example enum
enum class Color

    red, green, blue, pink
;

// Example of two converter-container types
constexpr array colors1 = pairColor::red, "Red", pairColor::green, "Green", pairColor::blue, "Blue";
const map<Color, string> colors2  =  Color::red, "Red", Color::green, "Green", Color::blue, "Blue";


int main()

    cout << toString(colors1, Color::red) << " " << toString(colors2, Color::blue) << " "
     << toString(colors2, Color::pink) << endl;     

此代码按预期工作,并打印出“Red Blue NoMatch”

但是,我想重写 toString 函数,使其不返回字符串,而是返回存储在转换容器中的任何类型(例如 string、string_view 或 char*)。

如果没有 noMatch 部分,任务会很简单,只需使用“auto”而不是“string”作为返回类型:

template<typename Container, typename Index>
auto toString(Container&& names, Index&& index)

    for (auto&& elem : names) 
      if (elem.first == index) 
         return elem.second;
      
   
   return begin(names)->second;

这工作并打印出“红蓝红”,但不是我正在寻找的解决方案。我寻找的是一种拥有的方式:

template<typename Container, typename Index>
auto toString(Container&& names, Index&& index, const <returnType>& noMatch = "")

    for (auto&& elem : names) 
      if (elem.first == index) 
         return elem.second;
      
   
   return noMatch;

但正如你所见,我需要为 填写一些内容,但我不知道会是什么。 如果有帮助的话,我可以将函数分成两个不同的函数,一个用于映射,另一个用于数组/向量对,但我希望能够保持相同的调用语法。

FWIW,我们目前使用启用了 c++17 的 gcc 9.2。

谢谢。

【问题讨论】:

无论返回类型是什么,它在所有分支中都必须相同。在你的情况下它是 decltype(declval<:value_type>().second). 【参考方案1】:

只需推断second的类型,例如:

template<typename Container, typename Index,
         typename NoMatchType=decltype(std::declval<typename Container::value_type &&>().second)>
auto toString(Container&& names, Index&& index,
              const NoMatchType &noMatch=NoMatchType)

(如果您愿意,也可以在此处使用NoMatchType 代替auto

【讨论】:

我认为应该是declval&lt;typename Container::value_type&gt;().second #include &lt;string&gt;#include &lt;utility&gt;了吗? 感谢@StPiere 和@SamVarshavchik,但两者都不起作用。这两个建议都在“declval”上给出错误:error: 'const std::map&lt;Color, std::__cxx11::basic_string&lt;char&gt; &gt;&amp;' is not a class, struct, or union type 您可能缺少#include &lt;map&gt;。但是如果你能分享一下godbolt链接会更容易 我的答案可能需要调整,但是如果您了解此处显示的基本解决方案,您应该可以自己找出必要的调整。你熟悉declvaldecltype等吗?等等,这个语法试图做什么?【参考方案2】:

我发布了对@Sam 答案的改进,但归结为同一件事:

您可以使用一些相对容易理解的类型特征来实现您想要的,而不是第三个模板参数(这会使您的界面复杂化并锁定它以防止以后的简单扩展)。 而不是decltype/std::declval 语法对于它想要做的事情来说有点繁重,即获取Containervalue_type 的第二部分的类型。我们可以使用pairTupleLike 品质来简化这一点:

template<typename Container, typename Index>
auto toString(Container&& names, Index&& index,
              const std::tuple_element_t<1, typename Container::value_type>& noMatch = )

注意我省略了“重”参数类型的重复(在原始版本中已经不需要)。 如果你知道你只会对Container 使用MapLike 类型,你可以走捷径直接使用它的类型别名:

template<typename Container, typename Index>
auto toString(Container&& names, Index&& index,
              const typename Container::mapped_type& noMatch = )

【讨论】:

谢谢@rubenvb。经过一番努力,我得到了您的上述解决方案(这很好,因为这与其他任何事情一样都是一种学习经验):godbolt.org/z/6aYTs7 我需要做的是 1)在容器上使用 remove_reference_t - 和然后从 0 而不是 1 计数 - 从而在使用 tuple_element_t 时将索引从 2 更改为 1 ;-) 啊,是的,索引是我的错误,对此感到抱歉。 remove_reference_t 是必要的,因为您使用推断出的Container&amp;&amp;(并且没有错误或任何东西!)但是如果您使用例如const Container&amp; 然后remove_reference_t 就没有必要了。您在这些方面获得的经验越多,您在编写此类代码时就可以更好地解决您面临的问题。您也许可以避免一些问题,但您将继续遇到问题。很高兴你能从中学到东西!

以上是关于参数和返回类型中的模板类型推导的主要内容,如果未能解决你的问题,请参考以下文章

从std :: function中推导返回和参数类型作为模板函数参数传递?

什么是模板推导中的部分排序程序

深入理解函数模板

C++类模板常见用途和注意实现

模板之类模板

C++进阶第二十五篇——C++11(列表初始化+变量类型推导+右值引用和移动语义+新的类功能+可变模板参数)