参数和返回类型中的模板类型推导
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<typename Container::value_type>().second
你#include <string>
和#include <utility>
了吗?
感谢@StPiere 和@SamVarshavchik,但两者都不起作用。这两个建议都在“declval”上给出错误:error: 'const std::map<Color, std::__cxx11::basic_string<char> >&' is not a class, struct, or union type
您可能缺少#include <map>
。但是如果你能分享一下godbolt链接会更容易
我的答案可能需要调整,但是如果您了解此处显示的基本解决方案,您应该可以自己找出必要的调整。你熟悉declval
、decltype
等吗?等等,这个语法试图做什么?【参考方案2】:
我发布了对@Sam 答案的改进,但归结为同一件事:
您可以使用一些相对容易理解的类型特征来实现您想要的,而不是第三个模板参数(这会使您的界面复杂化并锁定它以防止以后的简单扩展)。
而不是decltype
/std::declval
语法对于它想要做的事情来说有点繁重,即获取Container
的value_type
的第二部分的类型。我们可以使用pair
的TupleLike
品质来简化这一点:
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&&
(并且没有错误或任何东西!)但是如果您使用例如const Container&
然后remove_reference_t
就没有必要了。您在这些方面获得的经验越多,您在编写此类代码时就可以更好地解决您面临的问题。您也许可以避免一些问题,但您将继续遇到问题。很高兴你能从中学到东西!以上是关于参数和返回类型中的模板类型推导的主要内容,如果未能解决你的问题,请参考以下文章