当类和函数具有单独的模板参数时,在类定义之外定义友元函数

cp.*_*.cp 5 c++ templates friend

以下代码可以正常编译(只是一个最小的示例,实际代码对这种特定布局有更多的原因):

template<int A>
class Foo {
    template<int B>
    friend int bar(Foo<A> a, Foo<B> b) {
        return A * B;
    }
};

int main() {
    return bar(Foo<0>(), Foo<1>());
}
Run Code Online (Sandbox Code Playgroud)

但是,我想将声明与定义分开,并将函数定义放在类定义之外。我尝试像对待成员函数一样这样做:

template<int A> template<int B>
int bar(Foo<A> a, Foo<B> b) {
    return A * B;
}
Run Code Online (Sandbox Code Playgroud)

但这无法编译,因为它似乎不是有效的语法:

error: too many template-parameter-lists
Run Code Online (Sandbox Code Playgroud)

但是,合并此定义 ( ) 中的两个模板参数列表template<int A, int B>会产生链接器错误:

undefined reference to `int bar<1>(Foo<0>, Foo<1>)'
Run Code Online (Sandbox Code Playgroud)

这让我相信不同的模板参数列表会导致编译器/链接器将 的定义解释为bar与声明不同的函数。

所以我的问题是:当类和函数具有单独的模板参数时,如何在类定义之外定义友元函数?

Rak*_*111 3

我认为这是没有办法的。嗯,有,但不通用。您始终可以显式地为每个编写重载A

template<int B>
int bar(Foo<0> a, Foo<B> b) {
    return 0 + B;
}
Run Code Online (Sandbox Code Playgroud)

基本上,原因是bar为 的每个实例定义了不同的重载Foo。例如,Foo<0>你会得到一个

template<int B>
int bar(Foo<0>, Foo<B>);
Run Code Online (Sandbox Code Playgroud)

对于 的每次实例化Foo,您都会获得一个新bar模板。所有这些bar都是独立的,因为它们使用存在于自身外部的模板参数bar(来自外部模板)。

您不能使用 doubletemplate<>语法,因为这意味着您有两个东西(类、函数……)是模板。这里的情况并非如此,因为您只有bar一个模板。第一个/第二个没有其他内容template<>

您无法合并它们,因为正如您所看到的,您将获得不同的功能。如果您查看bar上面的实例化Foo<0>,您会发现这与以下内容不同:

template<int A, int B>
int bar(Foo<A>, Foo<B>);
Run Code Online (Sandbox Code Playgroud)

实例化版本始终是更好的匹配,因为它不必为第一个参数推断出任何内容。