Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static_cast not working on precedence as expected

#include <iostream>
#include <cstdint>

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

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

int main()
{
  foo<static_cast<uint8_t>(42)> ();
  foo<static_cast<int>(42)>();
  return(0);
}

any idea why this isn't working as expected ?

My gcc 4.8.1 is complaining about an ambiguous call, but the static_cast isn't supposed to "fix" the precedence rule in cases like this one where you have 2 types with the same precedence ?

like image 939
user2485710 Avatar asked Jun 26 '13 23:06

user2485710


1 Answers

You would think that the compiler, when resolving overloaded function templates, tries to figure out which of the templates matches the given arguments better. Based on that assumption, a template with a uint8_t should match a function call with a uint8_t argument better than a template for int.

But that's not how template overload resolution works. Template overload resolution (§14.5.6.2) is different from ordinary function overload resolution (§13.3). It first establishes candidate templates and then, rather than trying to check how well each matches the given arguments, it simply establishes which of the two (or more) candidate templates is the most specialized.

Note that this is a matter between the candidate templates only. It does not take into account the given arguments of the function call. (Those are taken into account for type deduction, which is only part of the procedure that establishes the candidate set of templates.)

So it checks whether uint8_t is more specialized than int or vice versa (in general – not with respect to the given arguments of the function call at hand). It does this, basically, by checking whether any given uint8_t argument could (in theory) be used to fill an int parameter without non-standard conversions, and vice versa. This is the case (in both directions), so neither template is more specialized than the other. Hence the ambiguity cannot be resolved.


The relevant sections of the Standard are as follows.

First, §13.3.3 establishes that when two functions templates (as opposed to two ordinary functions, or one function and one template) compete for a function call, the template overload mechanism is used to select the best:

[...] a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

[...] — F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.

Then, §14.5.6.2 is very long, but the most relevant parts are:

(2) Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.

(3) To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (14.5.3) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template. [...]

(4) Using the transformed function template’s function type, perform type deduction against the other template as described in 14.8.2.4.

So the idea here is: Take the uint8_t template and transform it by replacing the uint8_t parameter with an actual, synthesized value (I guess that value could be taken from the actual function call, but the Standard doesn't say that). Then use the type deduction process to check if the transformed template, taken as a function call, would "match" the other template (i.e. the int template), i.e. if the int parameter of the other template could be deduced without non-standard conversions. The answer is yes, it could.

Then go the other way, take the int template, synthesize a value and try if this "matches" the uint8_t template, i.e. if the uint8_t parameter could be deduced without non-standard conversions. The answer is yes again.

If this works in only one direction, one of the two templates must be more specialized than the other and is chosen to resolve the ambiguity. If it works both ways (as in your case), the ambiguity cannot be resolved.

Note. The entire procedure is in fact more complicated, and its description in the Standard very long, mainly for the following reasons:

  • The type deduction process itself is complicated. It does allow for certain implicit conversions (basically, standard-conversions including some related to cv-qualifiers);
  • It has a number of special arbitration rules for the case when one candidate parameter is a const-reference and the other in a non-const reference, and some similar cases;
  • Each candidate template may give rise to multiple transformed templates, especially when there is more than one template parameter to be deduced;
  • The presence of default-arguments as well as template parameter packs complicates the situation further.
like image 61
jogojapan Avatar answered Oct 21 '22 00:10

jogojapan