【问题标题】:Undefined reference to specialized template member对专用模板成员的未定义引用
【发布时间】:2018-08-27 18:47:51
【问题描述】:

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

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 用于已经专门化 A 的任何模板模板类 F

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&lt;B&lt;E&gt;::C&gt;::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();
}

【问题讨论】:

    标签: c++ templates partial-specialization template-templates


    【解决方案1】:
    template<class T>
    struct A {};
    
    template<class T>
    struct B {
      using type=T;
    };
    
    template<class T>
    struct A<typename B<T>::type> {};
    

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

    这也不行。

    问题在于B&lt;T&gt;::typeB&lt;T&gt;::template Z 或其他任何东西,在一般情况下,是一个任意的编译时函数。

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

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

    为了离开

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

    要查看 A&lt;foo&gt; 是否匹配它,它必须测试所有类型 T 以查看其中哪些具有等于 fooB&lt;T&gt;::type

    这可能不是您打算要求的,但这正是您所要求的。

    您的模板示例也是如此。

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

    您要求编译器检查每个类型F,这样如果您将其传递给任意模板B&lt;&gt;,然后在其中评估::C,模板是否与您传递的A 匹配。

    第一个有趣的案例:

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

    现在,A&lt;C0&gt; 中的 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&lt;C0&gt; 进行模式匹配,编译器必须生成F,这样F&lt;int&gt;::value 是一种编译时类型,当传递给proves_collatz_conjecture 时会在编译时返回true

    那会很有用。


    模板特化是模式匹配。在 C++ 中,您不能对依赖类型(可能还有模板)进行模式匹配,因为类型和模板都没有超出其值的标识。

    您无法检查定义变量、类型或模板的范围。因此您也无法进行模式匹配。

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

    【讨论】:

    • 我还是有点迷茫。在我给出的失败示例(A&lt;B&lt;E&gt;::template C&gt;::foo())中,编译器在替换时是否应该知道F = E,因为我给它一个具体的类型?所以它不必检查每个F,它只需要检查E。当您在专业化中将E 替换为F 时,它与示例完美匹配。
    • @Taylor 模板获取类型,而不是您生成该类型的方式。模板C 可以命名 B&lt;E&gt;::C,但该名称不能从C 模板的实例中访问。最重要的是,假设您添加了template&lt;class&gt;struct X{}; template&lt;&gt; struct B&lt;X&gt;:B&lt;E&gt;{};——现在B&lt;X&gt;::CB&lt;E&gt;::C 是完全相同的模板。 F=XF=E 都匹配该专业。无论如何,标准中的规则是模板模式匹配不会反转依赖类型。
    猜你喜欢
    • 2011-05-05
    • 2012-03-10
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 2021-12-13
    相关资源
    最近更新 更多