Domanda

I have a problem where I'd like to provide a generic version of a function foo which may only be applied when there is absolutely no other match for an invocation. How can I modify the following code such that last_resort::foo is a worse match for derived::type than base::foo? I'd like to find a solution which does not involve modifying the definition of bar and which would preserve the type of the argument of last_resort::foo.

#include <iostream>

namespace last_resort
{

template<typename T> void foo(T)
{
  std::cout << "last_resort::foo" << std::endl;
}

}

template<typename T> void bar(T)
{
  using last_resort::foo;
  foo(T());
}

namespace unrelated
{

struct type {};

}

namespace base
{

struct type {};

void foo(type)
{
  std::cout << "base::foo" << std::endl;
}

}

namespace derived
{

struct type : base::type {};

}

int main()
{
  bar(unrelated::type()); // calls last_resort::foo
  bar(base::type());      // calls base::foo
  bar(derived::type());   // should call base::foo, but calls last_resort::foo instead

  return 0;
}
È stato utile?

Soluzione 4

last_resort::foo can be removed from the overload set with disable_if. The idea is to disable last_resort::foo(T) if foo(T) is otherwise well-formed. This causes last_resort::foo(T) to be the worst match for foo:

namespace test
{

template<typename T> struct has_foo { ... };

}

namespace last_resort
{

template<typename T>
  struct disable_if_has_foo
    : std::enable_if<
        !test::has_foo<T>::value
      >
{};

template<typename T>
  typename disable_if_has_foo<T>::type foo(T)
{
  std::cout << "last_resort::foo" << std::endl;
}

}

The output:

$ g++ last_resort.cpp 
$ ./a.out 
last_resort::foo
base::foo
base::foo

This answer describes how to build a solution for checking for the existence of a function (foo) returning void.

Altri suggerimenti

This would be about as bad as it gets:

struct badParam { template <typename T> badParam(T t) { } };
namespace last_resort {
  void foo(badParam, int dummy = 0, ...) {
    std::cout << "last_resort::foo" << std::endl;
  }
}

You've got a user-defined conversion, a defaulted parameter and an unused ellipsis.

[edit]

Slight variant, to save T I moved the user-defined conversion to the dummy parameter:

struct badParam { 
    badParam() { }
    operator int() { return 42; }
};
namespace last_resort {
  template <typename T> void foo(T t, int dummy = badParam(), ...) {
    std::cout << "last_resort::foo" << std::endl;
  }
}

You can't do much about it. Both foo functions are in the overload set. But your last_resort one is a better match simply because it does not require a conversion unlike base::foo for derived::type(). Only in the case where two candidates are "equally good" judging by the parameters and possible conversions, a non-template is preferred.

You can provide an overload of bar for type derived::type after the declaration of derived::type. This can be in namespace derived or not.

void bar(derived::type)
{
  foo(derived::type());
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top