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

Tay*_*lor 4 c++ templates partial-specialization template-templates

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

template <template <typename> class F>
struct A {
  static int foo();
};
Run Code Online (Sandbox Code Playgroud)

此类没有默认定义foo,必须专门针对不同类型.

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

template <template <typename> class F>
struct B {
  template <typename T>
  struct C {};
};
Run Code Online (Sandbox Code Playgroud)

我想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;
}
Run Code Online (Sandbox Code Playgroud)

所以,如果我有一个专门的课程A:

template <typename T>
struct E {};

template <>
int A<E>::foo() {
  return 42;
}
Run Code Online (Sandbox Code Playgroud)

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

int bar() {
  return A<B<E>::template C>::foo();
}
Run Code Online (Sandbox Code Playgroud)

但是,这无法链接 - 它找不到引用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();
}
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 5

template<class T>
struct A {};

template<class T>
struct B {
  using type=T;
};

template<class T>
struct A<typename B<T>::type> {};
Run Code Online (Sandbox Code Playgroud)

这基本相同,但模板层少1个.

这也不起作用.

问题在于,B<T>::type或者B<T>::template Z在一般情况下是任意编译时函数.

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

标准说"编译器不必这样做",这是你可以在这里做的少数理智的事情之一.它绝对说明了类型; 对于模板,嗯,标准的模板模板参数的措辞往往缺少细节,所以如果缺少措辞,我不会感到惊讶.但如果没有,那将是标准中的一个错误.

为了从中走

template<class T>
struct A<typename B<T>::type> {};
Run Code Online (Sandbox Code Playgroud)

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

这可能不是你打算问的问题,但这就是你要求的.

您的模板示例也是如此.

template <template <typename> class F>
struct A<B<F>::template C> {
  static int foo();
};
Run Code Online (Sandbox Code Playgroud)

你要求编译器检查每个类型F,如果你将它传递给任意模板B<>然后::C在其中进行评估,模板是否匹配你传递的内容A.

第一个有趣的案例

template<class X>
struct C0 {};
template <template <typename> class F>
struct B {
  template <typename T>
  using C=C0<X>:
};
Run Code Online (Sandbox Code Playgroud)

现在,是什么FA<C0>?每个人都有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>;
};
Run Code Online (Sandbox Code Playgroud)

现在要模式化A<C0>,编译器必须生成F这样F<int>::value一个编译时类型,在传递给编译时proves_collatz_conjecture返回true.

那将是有用的.


模板专业化是模式匹配.在C++中,您无法对依赖类型(以及可能是模板)进行模式匹配,因为类型和模板都没有超出其值的标识.

您无法检查定义变量,类型或模板的范围.因此您也无法模式匹配.

如果你想做你想做的事情,模板C本身就有一个你可以检查和测试的属性.