问题
I thought that a non-template would always take precedence over a template, if the arguments match up just as well.
But:
template <typename... Args>
void Trace(Args&&... args)
{
throw "what the frak";
}
void Trace(const int&)
{}
int main()
{
Trace(42);
}
This throws unless I make the non-template Trace(int)
or Trace(int&&)
, i.e. not taking a const ref.
It's kind of annoying because I want to provide a different implementation for specific argument types where the real implementation of the template does not make sense (and, in fact, would not compile).
I can fix it by making the second function a specialisation, but only if I match argument types by taking an rvalue reference:
template <>
void Trace(int&&)
{}
And, well, maybe I didn't want to do that! Maybe I want to call it with a const
thing sometimes and would rather not specialise twice.
Is it correct and standard-abiding that simply providing a non-template overload works this way, in this example? It's the first time I've noticed this behaviour (not that that necessarily means anything!).
回答1:
42
is an rvalue of type int
, therefore it binds more closely to int&&
rather than const int&
. This is why it is calling your template Trace
function.
If you were to call
const int i{};
Trace(i);
then your Trace(const int&)
overload would be invoked.
Possible workarounds:
Add a
Trace(int&&)
overload that invokesTrace(const int&)
. You might also need aTrace(int&)
;Use SFINAE on the templated
Trace
to prevent instantiation when the first argument is anint
;template <typename Arg, typename... Args> auto Trace(Arg&& arg, Args&&... args) -> std::enable_if_t<!std::is_same_v<std::decay_t<Arg>, int>> { throw "what the frak"; }
Change the templated
Trace
to takeconst Args&...
instead.
来源:https://stackoverflow.com/questions/57888165/forwarding-template-taking-precedence-over-overload