使foo(derived_object)调用foo(Base const&)而不是模板函数? [英] Make foo(derived_object) call foo(Base const&) instead of template function?

查看:185
本文介绍了使foo(derived_object)调用foo(Base const&)而不是模板函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定此代码:

template< class C >
void foo( C const& o ) { o.nosuch(); }

struct Base {};
void foo( Base const& ) {}

struct Derived: Base {};

auto main() -> int
{
    Derived d;
    foo( d );       // !Invokes template.
}

…我想调用来调用为 Base 定义的重载,而不必为 Derived 定义重载或模板专用化。

… I want the call to invoke the overload defined for Base, without having to define an overload or template specialization for Derived.

目标是能够将 foo 应用于所有类型的对象,而不仅仅是 Base (和 Derived )对象,对大多数类型的对象使用通用实现。

The goal is to be able to apply foo to all kinds of objects, not just Base (and Derived) objects, with a generic implementation for most kinds of objects.

如果一个答案解释了在这种情况下如何使用重载解决方案,以及如何使用定义的 Derived 重载来解决这个问题,那将会很好。

It would also be nice if an answer explained exactly how overload resolution works in this case, versus how it works with a Derived overload defined.

在出现此问题的代码中,上面的 foo 是一个函数模板 n_items 定义如下:

In the code where this problem manifested, foo above is a function template n_items defined as follows:

template< class Type >
auto n_items( Type const& o )
    -> size_t
{ return o.size(); }

template< size_t n >
auto n_items( std::bitset<n> const& o )
    -> size_t
{ return o.count(); }       // Corresponds to std::set<int>::size()

template< class Type, size_t n >
constexpr
auto n_items( Raw_array_of_<n, Type> const& a )
    -> size_t
{ return static_size( a ); }

这样做的目的是,自己的 n_items 重载。

And the intent is that this should be a catch-all for types that don't define their own n_items overloads.

对于一个基类和派生类, custom n_items ;

And for a base and derived class, it should be enough that the base class defines a custom n_items; it would be very redundant to have to define it for every derived class.

推荐答案

如何重载解析工作



首先我们做名称查找和模板类型扣除。对于 foo(d),这给了我们两个选项:

How overload resolution works

First we do name lookup and template type deduction. For foo(d), this gives us two options:


  1. foo(Derived const&) [C = Derived]

  2. foo(Base const&)

  1. foo(Derived const&), with [C = Derived]
  2. foo(Base const&)

然后我们确定哪一个重载是最好的可行候选人。这首先通过查看转换序列来完成。在这种情况下,(1)是完全匹配,而(2)涉及具有转换排名的派生到基础转换(参见此表)。由于排名更差,候选(1)是优选的,因此被认为是最好的可行候选。

Then we determine which one of the overloads is the best viable candidate. This is done first by looking at conversion sequences. In this case, (1) is an Exact Match whereas (2) involves a derived-to-base conversion which has Conversion rank (see this table). Since that ranks worse, candidate (1) is preferred and is thus deemed the best viable candidate.

最简单的方法是简单地添加第三个重载 Derived

The simplest way is to simply add a third overload for Derived:


  1. ;)

  1. foo(Derived const&)

这里,(1)和(3) 。但是不是模板的函数优先于模板的函数(认为它是选择最具体的选项),所以选择(3)。

Here, both (1) and (3) would be equivalent in terms of conversion sequence. But functions that aren't templates are preferred to functions that are templates (think of it as picking the most specific option), so (3) would be selected.

但是你不想这样做。所以选项是:make(1)不适用于 Derived 或make(2)也适用于 Derived

But you don't want to do that. So the options are either: make (1) not work for Derived or make (2) work for Derived too in a way that's preferred.



禁用一般模板



我们可以使用SFINAE简单地排除从 Base

template <class T, class = std::enable_if_t<!std::is_convertible<T*, Base*>::value>
void foo(T const& ); // new (1)

void foo(Base const& ); // same (2)

现在, $ c>不再是匹配的可行候选,所以(2)是trivally首选。

Now, (1) is no longer a viable candidate for the match, so (2) is trivally preferred.

取出 Xeo的书,我们可以这样重组重载:

Taking a tip out of Xeo's book, we can restructure the overloads thusly:

template <int I> struct choice : choice<I + 1> { };
template <> struct choice<10> { };
struct otherwise { otherwise(...) {} };

template <class T> void foo(T const& val) { foo_impl(val, choice<0>{}); }

现在我们更喜欢从 Base

template <class T, class = std::enable_if_t<std::is_convertible<T*, Base*>::value>>
void foo_impl(T const& val, choice<0> ); // new (2)

template <class T>
void foo_impl(T const& val, otherwise ); // new (1)

这会改变重载解析的工作机制,

This changes the mechanics of how overload resolution works and is worth going through into the separate cases.

调用 foo(d)意味着我们调用 foo_impl d,choice< 0>)并且给我们两个可行的候选者:

Calling foo(d) means we're calling foo_impl(d, choice<0> ) and gives us two viable candidates:


  1. foo_impl (Derived const& choice;< 0>) [T = Derived]

  2. foo_impl(Derived const& amp;其他) [T = Derived]

  1. foo_impl(Derived const&, choice<0> ), with [T = Derived]
  2. foo_impl(Derived const&, otherwise ), with [T = Derived]

这里,第一个参数是相同的,但对于第二个参数,第一个重载是一个完全匹配,而第二个参数需要一个可变参数的转换。 否则将总是作为结果的单词选择,因此首选重载。正确的是我们想要的。

Here, the first argument is identical, but for the second argument, the first overload is an Exact Match while the second argument requires a Conversion via a variadic argument. otherwise will always be the word choice as a result, so the first overload is preferred. Exactly what we want.

另一方面,调用 foo(not_a_base) > one 可行候选人:

Calling foo(not_a_base), on the other hand, would only give us one viable candidate:


  1. foo_impl(NotABase const& c>,与 [T = NotABase]

  1. foo_impl(NotABase const&, otherwise ), with [T = NotABase]

由于扣除失败。所以这是简单的最可行的候选人,我们再次得到我们想要的是什么。

The other one was removed due to the deduction failure. So this is trivially the best viable candidate, and again we get exactly what we want.

对于您的具体问题,我会写下:

For your specific question, I'd write something like:

template <class T>
size_t n_items(T const& cont) { return n_items(cont, choice<0>{}); }

// has count?
template <class T>
auto n_items(T const& cont, choice<0> ) -> decltype(cont.count()) {
    return cont.count();
}

// else, has size?
template <class T>
auto n_items(T const& cont, choice<1> ) -> decltype(cont.size()) {
    return cont.size();
}

// else, use static_size
template <class T>
size_t n_items(T const& cont, otherwise )
    return static_size(cont);
}

这篇关于使foo(derived_object)调用foo(Base const&amp;)而不是模板函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆