ADL and template instantiation

63 visualizações
Pular para a primeira mensagem não lida

Eric Niebler

não lida,
1 de jul. de 2012, 00:42:4501/07/2012
para std-dis...@isocpp.org
Today I encountered a new and exciting strangeness in C++: argument dependent lookup can cause template instantiation *of template parameters*, which can lead to a hard error. Observe:

    template<typename T>
    struct wrap
    {
        typedef typename T::type type;
    };

    template<typename Fun>
    struct function
    {
    };

    template<typename F>
    void fun(F const &f)
    {}

    int main()
    {
        function<wrap<int>(int)> f;
        fun(f); // ERROR: ADL causes erroneous instantiation of wrap<int>
    }

Since the call to fun is unqualified, it uses ADL, which searches the associated namespaces. It examines the type wrap<int> to find the associated namespaces, and in so doing it causes the template to be instantiated. But int is not a valid template argument to wrap, so the compilation fails. You can verify that it is in fact ADL that is causing the problem because when you qualify the call to fun, compilation succeeds. Wacky. I've tried this on 3 compilers (msvc 10, clang trunk, comeau online), and they all behave identically.

My question is this: why does ADL need to instantiate wrap<int> to find its associated namespaces? IIUC, template parameters do indeed get used to populate the list of associated namespaces, but not the base classes of template parameters. So it seems to me that collecting the set of associated namespaces from template parameters does not requires any types found there to be complete. What have I missed?

Thanks,
Eric


Daniel Krügler

não lida,
1 de jul. de 2012, 13:35:4901/07/2012
para std-dis...@isocpp.org
2012/7/1 Eric Niebler <eric.n...@gmail.com>:
> Today I encountered a new and exciting strangeness in C++: argument
> dependent lookup can cause template instantiation *of template parameters*,
> which can lead to a hard error. Observe:
>
> template<typename T>
> struct wrap
> {
> typedef typename T::type type;
> };
>
> template<typename Fun>
> struct function
> {
> };
>
> template<typename F>
> void fun(F const &f)
> {}
>
> int main()
> {
> function<wrap<int>(int)> f;
> fun(f); // ERROR: ADL causes erroneous instantiation of wrap<int>
> }
>
> Since the call to fun is unqualified, it uses ADL, which searches the
> associated namespaces. It examines the type wrap<int> to find the associated
> namespaces, and in so doing it causes the template to be instantiated. But
> int is not a valid template argument to wrap, so the compilation fails. You
> can verify that it is in fact ADL that is causing the problem because when
> you qualify the call to fun, compilation succeeds. Wacky. I've tried this on
> 3 compilers (msvc 10, clang trunk, comeau online), and they all behave
> identically.

You can add gcc 4.8 to this list as well.

> My question is this: why does ADL need to instantiate wrap<int> to find its
> associated namespaces? IIUC, template parameters do indeed get used to
> populate the list of associated namespaces, but not the base classes of
> template parameters. So it seems to me that collecting the set of associated
> namespaces from template parameters does not requires any types found there
> to be complete. What have I missed?

My understanding is that they behave this way because for associated classes
the compiler has to respect visibility of potential friend functions
as described in
3.4.2 p4 b2.

- Daniel

Johannes Schaub

não lida,
1 de jul. de 2012, 15:02:0701/07/2012
para std-dis...@isocpp.org


On Sunday, July 1, 2012 6:42:45 AM UTC+2, Eric Niebler wrote:
Today I encountered a new and exciting strangeness in C++: argument dependent lookup can cause template instantiation *of template parameters*, which can lead to a hard error. Observe:

    template<typename T>
    struct wrap
    {
        typedef typename T::type type;
    };

    template<typename Fun>
    struct function
    {
    };

    template<typename F>
    void fun(F const &f)
    {}

    int main()
    {
        function<wrap<int>(int)> f;
        fun(f); // ERROR: ADL causes erroneous instantiation of wrap<int>
    }

Since the call to fun is unqualified, it uses ADL, which searches the associated namespaces. It examines the type wrap<int> to find the associated namespaces, and in so doing it causes the template to be instantiated. But int is not a valid template argument to wrap, so the compilation fails. You can verify that it is in fact ADL that is causing the problem because when you qualify the call to fun, compilation succeeds. Wacky. I've tried this on 3 compilers (msvc 10, clang trunk, comeau online), and they all behave identically.


You may find it interesting to read also http://llvm.org/bugs/show_bug.cgi?id=9440 . 
 
My question is this: why does ADL need to instantiate wrap<int> to find its associated namespaces? IIUC, template parameters do indeed get used to populate the list of associated namespaces, but not the base classes of template parameters.

The Standard says that the classes and namespaces associated with "A<B>" include the classes and namespaces associated with "B". If B is an "C<D>" or "C<D>(int)" (as in your case), it includes those associated with "C<D>". And those are "If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes.". Hence base classes are included into that set. 

I'm not sure whether I'm missing something, but that's how I've ever been reading that paragraph.
 
So it seems to me that collecting the set of associated namespaces from template parameters does not requires any types found there to be complete. What have I missed?


Of course, it also applies what Daniel Krügler said. 
 

Eric Niebler

não lida,
1 de jul. de 2012, 17:19:0701/07/2012
para std-dis...@isocpp.org
Indeed. Glad to know I'm not the only one to be caught out by this.

>> My question is this: why does ADL need to instantiate wrap<int> to
>> find its associated namespaces? IIUC, template parameters do indeed
>> get used to populate the list of associated namespaces, but not the
>> base classes of template parameters.
>
>
> The Standard says that the classes and namespaces associated with "A<B>"
> include the classes and namespaces associated with "B". If B is an
> "C<D>" or "C<D>(int)" (as in your case), it includes those associated
> with "C<D>". And those are "If T is a class type (including unions), its
> associated classes are: the class itself; the class of which it is
> a member, if any; and its direct and indirect base classes.". Hence base
> classes are included into that set.
>
> I'm not sure whether I'm missing something, but that's how I've ever
> been reading that paragraph.

It appears you are correct. Even base classes of template parameters are
used to populate the list of associated namespaces. That surprises me.

>> So it seems to me that collecting the set of associated namespaces
>> from template parameters does not requires any types found there to
>> be complete. What have I missed?
>
>
> Of course, it also applies what Daniel Krügler said.

Friend functions, right. Thanks all.

Eric
Responder a todos
Responder ao autor
Encaminhar
0 nova mensagem