C++ 中模板选择的优先级;
我刚刚编写了以下代码
template<typename T>
inline bool contains(T haystack,typename T::key_type needle) {
return haystack.find(needle) != haystack.end();
}
template<typename T>
inline bool contains(T haystack,typename T::value_type needle) {
return find(haystack.begin(),haystack.end(),needle) != haystack.end();
}
当我使用没有 key_type
typedef 的 vector
实例化模板时,SFINAE
将确保我不会实例化第一个版本。但是,如果我使用同时具有 key_type
和 value_type
typedef 的 map
实例化模板,该怎么办?编译器将如何选择使用哪个模板函数?
在当前的 STL 映射中,key_type
是一个 pair
,但是如果我定义一个 key_type
与 value_type< 相同的类型,会发生什么情况? /代码>?
class MyMap {typedef int key_type;typedef int value_type;};
MyMap m;
contains(m,1); // both functions are valid, which will be chosen?
令人惊讶的是,std::set
有key_type == value_type
。所以我真的需要求助于模板元编程才能拥有一个简单的 contains
函数。 叹息。
引用标准可加分。
I just written the following piece of code
template<typename T>
inline bool contains(T haystack,typename T::key_type needle) {
return haystack.find(needle) != haystack.end();
}
template<typename T>
inline bool contains(T haystack,typename T::value_type needle) {
return find(haystack.begin(),haystack.end(),needle) != haystack.end();
}
When I'm instantiating the template with vector
which doesn't have key_type
typedef, SFINAE
would make sure I wouldn't instantiate the first version. But what if I'm instantiating the template with a map
, which have both key_type
and value_type
typedefs? How will the compiler choose which template function to use?
With current STL map, key_type
is a pair
, however what happens if I define a type where key_type
is the same as value_type
?
class MyMap {typedef int key_type;typedef int value_type;};
MyMap m;
contains(m,1); // both functions are valid, which will be chosen?
And surprise surprise, std::set
has key_type == value_type
. So I really need to resort to template meta programming in order to have a simple contains
function. Sigh.
Bonus point for quoting the standard.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您愿意这样做,您可以通过一些元编程来帮助您。基本上,您需要编写一个模板元函数来确定要调用一个或其他函数的条件,并在
enable_if_c
子句中使用它:enable_if_c
模板是一个简单的常见 SFINAE 技巧(您可以从 C++0x 编译器或 boost 中使用)。它需要一个条件和一个类型,如果条件为真,它将生成参数类型的内部 typedef,如果不存在,则不会定义该内部类型,并且可以在 SFINAE 外部使用:现在有趣的是部分是如何确定类型
T
具有内部类型key_type
,虽然可能还有其他选项,但首先想到的是:模板
has_key_type
将包含一个内部常量仅当传递的类型 int 包含内部T::key_type
类型时,value
才为true
。解决方案并不太复杂:定义一个模板函数,该函数对于除您想要检测的类型之外的所有类型都会失败,并提供带有省略号的不同重载,以便在模板(具有比省略号更高的优先级)失败时捕获它代替。然后使用返回类型的大小来检测编译器选择了哪个重载。generate_ref
的存在是为了避免必须实际构造一个对象(即不要强制T
可以以任何特定方式构造)。总体结果是,当类型
T
包含内部类型key_type
时,has_key_type::value
的结果将为 true,并且enable_if_c
将启用第一个重载并禁用第二个重载,因此 SFINAE 将丢弃第二个重载,不是因为第二个函数参数类型未定义,而是因为返回类型。如果您想到这一点,就会发现围绕一个小更改只有一堆样板代码:不是提供两个不明确的模板函数重载,而是提供一个模板重载和一个较低优先级的函数(省略号)。由于您无法真正使用该省略号函数来实现非关联容器版本,因此它用于获取布尔值,然后将该布尔值植入到常见的元编程结构和样板中。
If you are willing to do it, you can, with some metaprogramming help to aid you. Basically you would need to write a template metafunction that determines the condition under which you want to call one or the other functions, and use that in an
enable_if_c
clause:The
enable_if_c
template is a simple common SFINAE trick (that you can use from a C++0x compiler or boost). It takes a condition and a type, if the condition is true, it will generate an inner typedef to the argument type, if it is not present it will not define that inner type, and that can be used outside in SFINAE:Now the interesting part is how to determine that a type
T
has an inner typekey_type
, and while there might be other options, the first that comes to mind is:The template
has_key_type
will contain an inner constantvalue
that istrue
only if the type passed int contains an innerT::key_type
type. The resolutions is not too complex: define a template function that will fail for all types but the one you want to detect, and provide a different overload with ellipsis so that it will catch if the template (has higher priority than the ellipsis) fails to substitute. Then use the size of the return types to detect which overload was chosen by the compiler. Thegenerate_ref
is there to avoid having to actually construct an object (i.e. do not impose thatT
can be constructed in any specific way).The overall result is that when the type
T
contains an inner typekey_type
, the result ofhas_key_type<T>::value
will be true, and theenable_if_c
will enable the first overload and disable the second, so SFINAE will discard the second overload not because of the type second function argument not being defined, but rather on terms of the return type.If you think of it, there is just a bunch of boiler plate code around a small change: instead of providing two ambiguous template function overloads, provide a template overload and a lesser priority function (ellipsis). Since you cannot really use that ellipsis function to implement the non-associative container version, it is used to obtain a boolean value that is then seeded into common metaprogramming structures and boilerplate.
key_type
模板匹配,而value_type
模板则不匹配。map::value_type
是一个pair
- 因此只有您的第一个模板会匹配。如果您的第二个模板使用例如mapped_type
,您会因不明确而出现编译器错误。The
key_type
template is a match, thevalue_type
template isn't.map<int,int>::value_type
is apair<const int,int>
- so only your first template will match. If your second template used e.g.mapped_type
you'd have a compiler error due to ambiguity.如果有两个或更多同样适合函数模板专门化的候选者,您将收到编译器错误。
C++ 标准说:
If there are two or more equally good candidates for function template specialization, you will get a compiler error.
C++ standard says:
在地图上使用它似乎有效。
但是,如果您要在 key_type 和 value_type 类型相同的某些容器上使用它,则最终会定义“相同”函数两次(并且在此基础上有所不同)。
§13.1
Using it on a map seems to work.
However, if you were to use it on some container where key_type and value_type were the same type, this would end up defining "the same" function twice (and differently on top of that).
§13.1