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!).
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 invokes Trace(const int&)
. You might also need a Trace(int&)
;
Use SFINAE on the templated Trace
to prevent instantiation when the first argument is an int
;
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 take const Args&...
instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With