对专用模板成员的未定义引用

Undefined reference to specialized template member

本文关键字:未定义 引用 成员 专用      更新时间:2023-10-16

我有一个由具有静态成员函数的模板模板类参数化的类:

template <template <typename> class F>
struct A {
static int foo();
};

这个类没有foo的默认定义,必须专门用于不同的类型。

我还有另一个由带有嵌套模板类的模板模板类参数化的类:

template <template <typename> class F>
struct B {
template <typename T>
struct C {};
};

我希望C专门针对任何专门A的模板模板类F专门A

template <template <typename> class F>
struct A<B<F>::template C> {
static int foo();
};
template <template <typename> class F>
int A<B<F>::template C>::foo() {
return A<F>::foo() / 2;
}

因此,如果我有一个专门A的课程:

template <typename T>
struct E {};
template <>
int A<E>::foo() {
return 42;
}

我希望能够像这样使用专业化(并返回 21(:

int bar() {
return A<B<E>::template C>::foo();
}

但是,这无法链接 - 它找不到对A<B<E>::C>::foo()的引用。

(请注意,所有这些都在一个文件中 - 这里的标头没有什么奇怪的事情发生(

编译器似乎正在尝试使用主模板进行A而不是专用化,这意味着foo未定义。为什么在这种情况下不使用专业化?

完整示例

template <template <typename> class F>
struct A {
static int foo();
};
template <template <typename> class F>
struct B {
template <typename T>
struct C {};
};
template <template <typename> class F>
struct A<B<F>::template C> {
static int foo();
};
template <template <typename> class F>
int A<B<F>::template C>::foo() {
return A<F>::foo() / 2;
}
template <typename T>
struct E {};
template <>
int A<E>::foo() {
return 42;
}
int bar() {
// Link fails - error: undefined reference to 'A<B<E>::C>::foo()'
return A<B<E>::template C>::foo();
}
template<class T>
struct A {};
template<class T>
struct B {
using type=T;
};
template<class T>
struct A<typename B<T>::type> {};

这基本上相同,但模板层少了 1 个。

这也行不通。

问题在于,在一般情况下,B<T>::typeB<T>::template Z或其他任何东西都是任意编译时函数。

为了与它进行模式匹配,我们需要反转这个任意编译时函数。

该标准说"编译器不必这样做",这是您可以在这里做的为数不多的理智的事情之一。 对于类型,它肯定是这样说的;对于模板,嗯,模板模板参数的标准措辞经常缺少细节,所以如果缺少措辞,我不会感到惊讶。 但如果它不这样做,那将是标准中的一个错误。

为了从

template<class T>
struct A<typename B<T>::type> {};

要查看A<foo>是否匹配它,它必须测试所有类型的T以查看其中哪些类型的B<T>::type等于foo.

这可能不是你打算问的,但这就是你要问的。

模板示例也是如此。

template <template <typename> class F>
struct A<B<F>::template C> {
static int foo();
};

您要求编译器检查每种类型F以便将其传递给任意模板B<>然后在其中::C进行计算,该模板是否与您传递A的内容匹配。

第一个有趣的案例:

template<class X>
struct C0 {};
template <template <typename> class F>
struct B {
template <typename T>
using C=C0<X>:
};

现在,A<C0>F是什么? 每个F都有资格。

template<class X>
struct C0 {};
template <template <typename> class F, class=void>
struct B {
template <typename T>
using C=C0<X>:
};
template<class X>
struct C1 {};
template <template <typename> class F, class=void>
struct B<
F,
std::enable_if_t<
proves_collatz_conjecture( F<int>::value )
>
> {
template <typename T>
using C=C1<T>;
};

现在,为了A<C0>模式,编译器必须生成F,以便F<int>::value是一个编译时类型,当传递给proves_collatz_conjecture时,在编译时返回true

那将是有用的。


模板专用化是模式匹配。 在C++中,您无法与依赖类型(大概是模板(进行模式匹配,因为类型和模板都没有超出其值的标识。

您无法检查定义变量、类型或模板的范围。 所以你也无法匹配模式。

如果要执行所需的操作,则模板C本身必须具有可以检查和测试的属性。