Identifying a single template specialization

  • Thread starter Johannes Schaub (litb)
  • Start date
J

Johannes Schaub (litb)

The desired outcome of the following is that it works (latest draft, n3126)

template<typename T> void f() { }
template<typename T> void g(T) { }

int main() { g(&f<int>); }

But I see a problem with that: When is the "&f" inspected in the gist of
14.8.1/3? We can have several places where we could apply it:

- Before argument dependent lookup is done. This would possibly affect
result of argument dependent lookup, because it might add associated classes
(if the function type of the specialization contains those classes).

- Before template argument deduction for the set of template candidates
found.

- After template argument deduction for the set of candidate functions
during overload resolution.

The Standard seems to be quiet about this, but there is a rationale to
reject the first and second bullet and to thus make the code ill-formed.

13.4 lists as one of the contexts that can be used to resolve a function
against a target type "a parameter of a function". In both the first and
second bullet, if we prematurely resolve "f<int>" using the arguments
provided in the template argument list, we would take away the possibility
of deducing "&f" later on to a concrete parameter. So we have to wait until
we get a final function template specialization against whose parameter we
could match by 13.4 and apply 14.8.2.1/6 in between:

"If the argument is an overload set containing one or more function
templates, the parameter is treated as a non-deduced context."

The above program would be ill-formed then because "T" cannot be deduced.
But the intent by 14.8.1/3 (as shown by its example code) is that the code
*is* valid, and that "f<int>" is resolved prior to this step, yet I miss
normative wording for both the case of deduction and the case of argument
dependent lookup.

As an example for the deduction case that yiels different results, consider
this

template<typename T = long> void f(void(*)(T));
template<typename U> void g(U);
template<typename U> void g(long);

int main() {
// - if resolved prematurely, this yields T = int
// - if resolved later, this yields T = long
f(0L, &g<int>);
}

Is this a defect, or am I just bloody blind again? Thanks for all your
insights!
 
A

Alf P. Steinbach /Usenet

* Johannes Schaub (litb), on 04.09.2010 14:28:
The desired outcome of the following is that it works (latest draft, n3126)

template<typename T> void f() { }
template<typename T> void g(T) { }

int main() { g(&f<int>); }

But I see a problem with that: When is the "&f" inspected in the gist of
14.8.1/3? We can have several places where we could apply it:

- Before argument dependent lookup is done. This would possibly affect
result of argument dependent lookup, because it might add associated classes
(if the function type of the specialization contains those classes).

- Before template argument deduction for the set of template candidates
found.

- After template argument deduction for the set of candidate functions
during overload resolution.

The Standard seems to be quiet about this, but there is a rationale to
reject the first and second bullet and to thus make the code ill-formed.

Don't know about the paragraph you refer to (I'd have to look it up), but the
way this works is that &f<int> is resolved as type void(*)(), then g<void(*)()>
is instantiated.



[snip rest]

Cheers & hth.,

- Alf
 
J

Johannes Schaub (litb)

Johannes said:
[snipped]
As an example for the deduction case that yiels different results,
consider this

template<typename T = long> void f(void(*)(T));
template<typename U> void g(U);
template<typename U> void g(long);

int main() {
// - if resolved prematurely, this yields T = int
// - if resolved later, this yields T = long
f(0L, &g<int>);
}

Is this a defect, or am I just bloody blind again? Thanks for all your
insights!

I guess I prematurely pressed the send button, haha. This call should read
"f(&g<int>)".

I think a further complication is the question whether partial ordering is
done on "g<int>" during processing of 14.8.1/3? If it's not done then this
would not be prematurely resolved because the requirement is that *a single*
function template specialization is found.

We can however use default arguments to make it valid for both cases to
yield a different result for each:

template<typename T = long> void f(void(*)(T));
template<typename U = int> void g(U);

int main() {
// - if resolved prematurely by 14.8.1/3, this yields T = int
// - if resolved later by 13.4, this yields T = long
f(&g<>);
}

The later resolving will see the target type as "void(*)(long)" and deduce
"g<>" to that, without needing to use the default argument. The premature
resolving will just resolve "g<>" using the default argument and deduce "T"
to int. Please forgive my premature acting.
 
J

Johannes Schaub (litb)

Alf said:
* Johannes Schaub (litb), on 04.09.2010 14:28:

Don't know about the paragraph you refer to (I'd have to look it up), but
the way this works is that &f<int> is resolved as type void(*)(), then
g<void(*)()> is instantiated.

Please see my follow-up post. My first post contained a flawed code example.
Even if partial ordering would be done on "&g<int>", in my post that would
give the same outcome for both cases because the second one is more
specialized and will thus give "void(*)(long)" as the function type.

My follow-up post contains an example which I believe is valid and its
outcome is unspecified.

You did snip my post and repeat the intended mechanism by the Standard. As
the DR that designed the wording of 14.8.1/3 and its example indicate, your
explanation is the intended mechanism. But I was wondering about the
normative wording. Is there any?
 
J

Johannes Schaub (litb)

Johannes said:
Johannes said:
[snipped]
As an example for the deduction case that yiels different results,
consider this
[snipped]
template<typename T = long> void f(void(*)(T));
template<typename U = int> void g(U);

int main() {
// - if resolved prematurely by 14.8.1/3, this yields T = int
// - if resolved later by 13.4, this yields T = long
f(&g<>);
}

Actually, I just tested clang and GCC, both coming up with different
results. Clang resolves it prematurely, and GCC resolves it later.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top