3
votes

Firstly, some standard's quoted passage

If a template specialization X is referenced in a context that depends on a template-parameter of some surrounding template Y, the given point of instantation depends on the point of instantation of Y.
If X is a function template specialization, the point of instantiation is that of Y.
If X is a class template specialization, the point of instantiation is immediately before the point of instantiation of Y.

Otherwise, the given point of instantiation is tied to the location of the namespace scope declaration/definition (D) which contains the statement referring to X.
If X is a function template specialization, the point of instantiation is immediately after D.
If X is a class template specialization, the point of instantiation is immediately before D.

Some code here

#include <iostream>
template<int N>
struct state {
    friend auto call(state<N>);
};
template<int N>
struct add_state {
    friend auto call(state<N>) {
        return N;
    }
};
template<typename T, int N>
T show() {
    add_state<N> d;
    return T{};
}
template<typename T,int N>
class data {
public:
    T c = show<T,N>();
};
#1,#3,#2
int main() {
    data<int, 0> t;
    call(state<0>{});
}

So, according to the above rules, when instantiating class data<int, 0>, then the point of instantiation is at #1.

Then show<T,N> depends on template class data's template parameters. So the point of instantiation for show<int,0> is at #2.

Then add_state<N> depends on template function show's template parameters. So according to the rules, the point of instantiation for add_state<0> is at #3.

At #3 auto call(state<0>) has been defined, call(state<0>{}) should be linked but in the fact, the compiler reports errors as follows:

clang:

main.cpp:24:2: error: function 'call' with deduced return type cannot be used before it is defined
        call(state<0>{});
        ^
main.cpp:4:14: note: 'call' declared here
        friend auto call(state<N>);
                    ^
1 error generated.

g++:

main.cpp: In function ‘int main()’:
main.cpp:24:17: error: use of ‘auto call(state<0>)’ before deduction of ‘auto’
  call(state<0>{});
                 ^ 

Why? Does my understand about the point of instantiation has some mistakes? If not, why does the compiler report these errors?

4
Since show is a function template, it reuses #1, and #3 precedes that.Davis Herring
Be warned: stateful metaprogramming has been condemned by the committee.Davis Herring
A function template specialization also has a point of instantiation at the end of the translation unit [temp.point]/5. If show<int,0> is instantiated at the end of your translation unit, the definition of call(state<0>) will be after the use in call(state<0>{});. [temp.point]/5 also forbids "two different points of instantiation [to] give a template specialization different meanings according to the one-definition rule", although I don't know whether this includes this scenario.walnut
Both GCC and Clang compile it fine if you use a placeholder return type for show to force the instantiation of the body: godbolt.org/z/jEuQ_kwalnut
@DavisHerring yeah,#1 and #2 are the same poixmh0511

4 Answers

2
votes

According to [temp.inst]/2.1, when a class template is implicitly instantiated, only the declaration of friends are instantiated:

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions, member classes, scoped member enumerations, static data members, member templates, and friends;

So at #3 auto call(state<N>) is only declared. Moreover, this declaration is not found by ordinary unqualified name look-up.

Nevertheless I do not think that it makes your code formaly ill-formed. Your code is so strange that it is possible that such a situation has never been thought about by standard commitee members or compiler implementers: usualy inline friend functions are defined in the class that makes the friend function visible through ADL (Argument Dependent Name Lookup). This is certainly also what excepects a compiler.

So at call(state<0>{}) inside main, the declaration of call is found by ADL inside the definition of state and the compiler just don't think about looking for a potential definition of this function in the somehow unrelated class add_state. So it fails to deduce auto.

0
votes

I'm not too confident myself in this matter, but in hope that this might prove useful I put together another working example, other than the ones already suggested:

#include <iostream>

// forward declaration of the
// add_state template
template<int>
struct add_state;


template<int N>
struct state {
    // Note: we generate the state here
    // so that the compiler will see the
    // definition of the call function
    add_state<N> t;
    friend auto call(state<N>);
};
template<int N>
struct add_state {
    friend auto call(state<N>) {
        return N;
    }
};


int main() {
    auto val = call(state<42>{});
    std::cout << val << std::endl;

    return 0;
}

I'm not sure if this will help. But I hope so, as I, too would be interested in a good explanation.

0
votes

Your issue is here:

template<int N>
struct state {
    friend auto call(state<N>);//<--no way of telling return type !
};

The compiler has absolutely no way of telling of what the call functions returns and has to give up. The fix is obvious as well, just give it something to work with, eg.:

friend auto call(state<N>) {return N;}
-2
votes

If used int instead of auto got error:

main.cpp:15: undefined reference to `call(state<0>)'
collect2.exe: error: ld returned 1 exit status

When added {return N;} to friend int call(state<N>), worked fine. Then replaced back int to auto, it also works.