对内联朋友的未定义引用

undefined reference to inline friend

为什么获取内联好友的地址不会导致生成代码。这适用于常规内联函数。声明是否与内联朋友不匹配?

#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

获取重载集的地址时没有 argument-dependent 查找。

所以,函数指针的初始化不需要编译器实例化X<256>来判断里面是否有operator+。对于不合格的函数调用,会发生这种情况。

operator+ 在 class 之外的声明也不需要任何涉及的类型是完整的。所以那里也没有 X<256> 的隐式实例化。

因此,当获取函数地址时,X<256>还没有被实例化,所以编译器无法知道函数的friend定义,这也是未实例化。

最后,函数没有定义,但是通过获取地址是 ODR-used,因此程序是 ill-formed,不需要诊断。

这可以通过在获取地址之前实例化 X<256> 来解决,例如通过在 main 之前写入 static_assert(sizeof(S::X<256>)); 以导致隐式实例化或 template S::X<256>; 以导致显式实例化。