C++ 中模板选择的优先级;

发布于 2024-10-29 17:31:24 字数 1050 浏览 2 评论 0原文

我刚刚编写了以下代码

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_typevalue_type typedef 的 map 实例化模板,该怎么办?编译器将如何选择使用哪个模板函数?

在当前的 STL 映射中,key_type 是一个 pair,但是如果我定义一个 key_typevalue_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::setkey_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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心在旅行 2024-11-05 17:31:24

如果您愿意这样做,您可以通过一些元编程来帮助您。基本上,您需要编写一个模板元函数来确定要调用一个或其他函数的条件,并在 enable_if_c 子句中使用它:

template <typename T> inline
typename enable_if_c< has_key_type<T>::value, bool >::type
contains( T const & c, T::key_type const & k ) {...}        // associative container

template <typename T> inline
typename enable_if_c< !has_key_type<T>::value, bool >::type
contains( T const & c, T::value_type const & k ) {...}      // non-associative container

enable_if_c 模板是一个简单的常见 SFINAE 技巧(您可以从 C++0x 编译器或 boost 中使用)。它需要一个条件和一个类型,如果条件为真,它将生成参数类型的内部 typedef,如果不存在,则不会定义该内部类型,并且可以在 SFINAE 外部使用:

template <bool condition, typename T>
struct enable_if_c {};        // by default do not declare inner type

template <typename T>
struct enable_if_c<true,T> {  // if true, typedef the argument as inner type
    typedef T type;
};

现在有趣的是部分是如何确定类型 T 具有内部类型 key_type,虽然可能还有其他选项,但首先想到的是:

template <typename T>
class has_key_type {
    typedef char _a;
    struct _b { _a x[2]; };

    template <typename U>
    static _a foo( U const &, typename U::key_type* p = 0 );
    static _b foo( ... );
    static T& generate_ref();
public:
    static const bool value = sizeof(foo(generate_ref())) == sizeof(_a);
};

模板 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:

template <typename T> inline
typename enable_if_c< has_key_type<T>::value, bool >::type
contains( T const & c, T::key_type const & k ) {...}        // associative container

template <typename T> inline
typename enable_if_c< !has_key_type<T>::value, bool >::type
contains( T const & c, T::value_type const & k ) {...}      // non-associative container

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:

template <bool condition, typename T>
struct enable_if_c {};        // by default do not declare inner type

template <typename T>
struct enable_if_c<true,T> {  // if true, typedef the argument as inner type
    typedef T type;
};

Now the interesting part is how to determine that a type T has an inner type key_type, and while there might be other options, the first that comes to mind is:

template <typename T>
class has_key_type {
    typedef char _a;
    struct _b { _a x[2]; };

    template <typename U>
    static _a foo( U const &, typename U::key_type* p = 0 );
    static _b foo( ... );
    static T& generate_ref();
public:
    static const bool value = sizeof(foo(generate_ref())) == sizeof(_a);
};

The template has_key_type will contain an inner constant value that is true only if the type passed int contains an inner T::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. The generate_ref is there to avoid having to actually construct an object (i.e. do not impose that T can be constructed in any specific way).

The overall result is that when the type T contains an inner type key_type, the result of has_key_type<T>::value will be true, and the enable_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.

蓝礼 2024-11-05 17:31:24

key_type 模板匹配,而 value_type 模板则不匹配。

map::value_type 是一个 pair - 因此只有您的第一个模板会匹配。如果您的第二个模板使用例如mapped_type,您会因不明确而出现编译器错误。

The key_type template is a match, the value_type template isn't.

map<int,int>::value_type is a pair<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.

赴月观长安 2024-11-05 17:31:24

如果有两个或更多同样适合函数模板专门化的候选者,您将收到编译器错误。

C++ 标准说:

如果恰好有一个可行的函数比
所有其他可行的函数,那么它就是通过重载选择的函数
解决;否则调用的格式不正确12)。

If there are two or more equally good candidates for function template specialization, you will get a compiler error.

C++ standard says:

If there is exactly one viable function that is a better function than
all other viable functions, then it is the one selected by overload
resolution; otherwise the call is ill-formed12).

森林迷了鹿 2024-11-05 17:31:24

在地图上使用它似乎有效。

但是,如果您要在 key_type 和 value_type 类型相同的某些容器上使用它,则最终会定义“相同”函数两次(并且在此基础上有所不同)。

§13.1

某些函数声明不能​​重载:
...
— 仅在使用等效 typedef“类型”方面不同的参数声明是等效的。一个
typedef 不是一个单独的类型,而只是另一个类型的同义词 (7.1.3)。
[ 示例:
typedef int Int;
void f(int i);
void f(Int i); // OK:重新声明 f(int)
void f(int i) { /* ... */ }
void f(Int i) { /* ... */ } // 错误:重新定义 f(int)
——示例结束]

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

Certain function declarations cannot be overloaded:
...
— Parameter declarations that differ only in the use of equivalent typedef “types” are equivalent. A
typedef is not a separate type, but only a synonym for another type (7.1.3).
[ Example:
typedef int Int;
void f(int i);
void f(Int i); // OK: redeclaration of f(int)
void f(int i) { /* ... */ }
void f(Int i) { /* ... */ } // error: redefinition of f(int)
—end example ]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文