Template function overload for base class [duplicate]

时光怂恿深爱的人放手 提交于 2019-12-10 13:25:32

问题


How do I force compiler to pick up a template function overload for a base class?

Here is an example that illustrates the question

#include <iostream>

class A
{};

class B : public A
{};

template <class T>
void f (const T& t)
{
    std::cout << "Generic f" << std::endl;
}

void f (const A& a)
{
    std::cout << "Overload for A" << std::endl;
}

template <class T>
void call_f (const T& t)
{
    f (t);  
}

int main() 
{
    call_f (10);
    call_f (A());
    call_f (B());

    return 0;
}

It produces the output

Generic f
Overload for A
Generic f

Why doesn't the compiler pick up f (const A&) in the 3rd case? UPD: OK, this one is clear void f<B> (const B&) is better than void f (const A&), but I'm still looking for answer to the 2nd question.

And is it possible to force it to do so without casting B to A?


回答1:


Using call_f(B()) results in a call to `f() which is best matched by the template version. For the non-template version a conversion is required. As a result, the template is chosen. If the template and the non-template would be equally good options, the non-template would be preferred.

If you want to the non-template to be called, you'll need to make the template a non-option. for example, the template could be implemented like

#include <type_traits>
template <class T>
typename std::enable_if<!std::is_base_of<A, T>::value>::type f(T const&)
{
    std::cout << "Generic f\n";
}

If C++11 can't be used you could either implement a version of std::is_base_of<...>, use a version from Boost or use a simple dispatch:

struct true_type {};
struct false_type {};

true_type A_is_base_of(A const*) { return true_type(); }
false_type A_is_base_of(void const*) { return false_type(); }

template <class T>
void f (T const&, false_type)
{
    std::cout << "Generic f\n";
}

void f (A const&, true_type)
{
    std::cout << "Overload for A\n";
}

template <class T>
void call_f (const T& t)
{
    f (t, A_is_base_of(&t));  
}



回答2:


I think this actually is possible. The trick is to make use of the fact that overload resolution prefers pretty much anything to a C-style variadic function argument. That way we can create helper functions that support tagged dispatch by constructing the appropriate tag for us. Overload resolution of the helper function forces the compiler to remove the generic template function from its list of candidate functions, leaving only the specialized function.

This is a lot more clear with code, so let's take a look.

#include <iostream>

struct foo {};
struct bar : public foo {};

struct generic_tag {};
struct     foo_tag {};

generic_tag make_tag(...) {
  return generic_tag();
}

foo_tag make_tag(foo const *) {
  return foo_tag();
}

template<typename T>
void f(T const &t, generic_tag) {
  std::cout << "Generic" << std::endl;
}

void f(foo const &f, foo_tag) {
  std::cout << "Specialized" << std::endl;
}

template<typename T>
void call_f(T const &t) {
  // This is the secret sauce.  The call to make_tag(t) will always
  // select the most specialized overload of make_tag() available.
  // The generic make_tag() will only be called if the compiler can't
  // find some other valid version of make_tag().

  f(t, make_tag(&t));
}

int main() {
  call_f(   10); // Prints "Generic"
  call_f(foo()); // Prints "Specialized"
  call_f(bar()); // Prints "Specialized"
}

I verified this solution works on Ununtu 14.04 using GCC 4.8.2.




回答3:


Assuming that you know that what you are calling call_f on will always be a derived type of A, you can simply explicitly ask for that version of the template, like so:

call_f<A> (B());

If you actually call it with a type that is not convertible to A, like a third class

class C {};

You should get a compiler error addressing this problem (along the lines of "error C2664: 'call_f' : cannot convert parameter 1 from 'C' to 'const A &")



来源:https://stackoverflow.com/questions/24290678/template-function-overload-for-base-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!