Identifying a single template specialization

Discussion in 'C++' started by Johannes Schaub (litb), Sep 4, 2010.

  1. 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!
     
    Johannes Schaub (litb), Sep 4, 2010
    #1
    1. Advertising

  2. * 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

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Sep 4, 2010
    #2
    1. Advertising

  3. Johannes Schaub (litb) wrote:

    > [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.
     
    Johannes Schaub (litb), Sep 4, 2010
    #3
  4. Alf P. Steinbach /Usenet wrote:

    > * 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.
    >


    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?
     
    Johannes Schaub (litb), Sep 4, 2010
    #4
  5. Johannes Schaub (litb) wrote:

    > Johannes Schaub (litb) wrote:
    >> [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.
     
    Johannes Schaub (litb), Sep 4, 2010
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. David B. Held
    Replies:
    2
    Views:
    470
    Rob Williscroft
    Oct 26, 2003
  2. Dave
    Replies:
    4
    Views:
    7,677
    pdixtl
    Jun 4, 2010
  3. pit3k
    Replies:
    8
    Views:
    366
    Victor Bazarov
    Feb 9, 2005
  4. case2005
    Replies:
    3
    Views:
    1,808
    Nicolas Pavlidis
    Feb 13, 2005
  5. Joseph Turian
    Replies:
    2
    Views:
    467
Loading...

Share This Page