Home > Software engineering >  undefined reference to inline friend
undefined reference to inline friend

Time:05-05

Why does taking the address of an inline friend not result in code being produced. This works for regular inline functions. Is the declaration not matching the inline friend somehow?

#include <iostream>

namespace S
{

template <unsigned N>
class X
{
    int i;

    friend X operator (const X& a, const X& b) noexcept
    {
        (void)a;
        (void)b;
        return {};
    }
};

inline X<256> operator (const X<256>&, const X<256>&) noexcept;

}

int main()
{
    S::X<256> (*ptr)(const S::X<256>& a, const S::X<256>& b) noexcept = &::S::operator ;
    std::cout << (void*)ptr << std::endl;

    return 0;
}
$ g   --version
g   (GCC) 11.2.1 20220401 (Red Hat 11.2.1-10)
$ g   -std=c  17 test2.cpp 
test2.cpp:18:13: warning: inline function ‘S::X<N> S::operator (const S::X<N>&, const S::X<N>&) [with unsigned int N = 256]’ used but never defined
   18 | inline X<N> operator (const X<N>&, const X<N>&) noexcept;
      |             ^~~~~~~~
/usr/bin/ld: /tmp/ccoA1Nxj.o: in function `main':
test2.cpp:(.text 0xc): undefined reference to `S::X<256u> S::operator <256u>(S::X<256u> const&, S::X<256u> const&)'
collect2: error: ld returned 1 exit status

CodePudding user response:

There is no argument-dependent lookup when taking the address of an overload set.

So, the initialization of the function pointer doesn't require the compiler to instantiate X<256> to figure out whether there is a operator in it. For an unqualified function call this would happen.

Neither does the declaration of operator outside the class require any of the involved types to be complete. So there is no implicit instantiation of X<256> there either.

Consequently, when the address of the function is taken, X<256> has not been instantiated and so the compiler can't be aware of the friend definition of the function, which is then also not instantiated.

In the end, there is no definition for the function, which however was ODR-used by taking its address and therefore the program is ill-formed, no diagnostic required.

This can be fixed by having X<256> be instantiated before taking the address, e.g. by writing static_assert(sizeof(S::X<256>)); before main to cause implicit instantiation or template S::X<256>; to cause explicit instantiation.

  • Related