cpp 文件中的部分专业化不是 "well-formed"

Is partial specialization in a cpp file not "well-formed"

follow-up [问题]: .

我在 .cpp 文件中使用部分特化来处理特殊情况,而不是将所有代码强制放入 header 文件中。下面给出了我正在做的一个简化示例。这适用于 gcc 和 clang,但给出了上述问题的答案,我想知道我是否只是幸运地发现链接器正在寻找兼容的符号。

我从 foo.hpp 中定义的 class 模板开始:

#pragma once

template <typename T1, typename T2>
class foo
{
public:
  foo (T1 t1, T2 t2) : d_t1(t1), d_t2(t2) {}
  ~foo () = default;

  void print ();
private:
  T1 d_t1;
  T2 d_t2;
};

请注意,print() 方法已声明,但未定义。

现在在 foo.cpp 中,我定义了 print() 方法。

#include <iostream>

template <typename T1, typename T2>
void
foo<T1,T2>::print()
{
    std::cout << "T1 is: ";
    d_t1.print();
    std::cout << std::endl;
    std::cout << "T2 is: ";
    d_t2.print();
    std::cout << std::endl << std::endl;
}

请注意,此实现假设 T1 和 T2 都有一个名为 print() 的方法。在我的真实世界代码中,用作此模板参数的类型通常符合此接口,但有时它们不符合此接口,我通过 .cpp 文件中的部分特化来处理该接口。这个模板并不意味着真正的通用用法,它只需要使用我知道的少数类型 up-front。

例如,我可能有 3 种类型,例如

class HasPrint
{
public:
  HasPrint () = default;
  ~HasPrint () = default;

  void print ();

};

class AlsoHasPrint
{
public:
  AlsoHasPrint () = default;
  ~AlsoHasPrint () = default;

  void print ();

};

class NoPrint
{
public:
  NoPrint () = default;
  ~NoPrint () = default;

  void noPrint ();

};

注意 'HasPrint' 和 'AlsoHasPrint' class 有一个 print() 方法,而 'NoPrint' class 有一个 noPrint() 方法。在这些 classes 的主体中,print/noPrint 方法只是打印 class.

的名称

为了处理有人使用 NoPrint 作为模板参数之一的情况,我在 foo.cpp 文件中定义了部分特化:

template <typename T2>
class foo <NoPrint, T2>
{
public:
  foo (NoPrint n1, T2 t2) : d_n1(n1), d_t2(t2) {}
  ~foo () = default;

  void print ()
  {
    std::cout << "NoPrint is: ";
    d_n1.noPrint();
    std::cout << std::endl;
    std::cout << "T2 is: ";
    d_t2.print();
    std::cout << std::endl;
  }
private:
  NoPrint d_n1;
  T2 d_t2;
};

然后为了获得我需要为我使用的所有排列生成的所有代码,我包括(在 foo.cpp 文件中)以下 foo.

的显式实例化
template class foo<HasPrint, HasPrint>;
template class foo<HasPrint, AlsoHasPrint>;
template class foo<AlsoHasPrint, AlsoHasPrint>;
template class foo<NoPrint, HasPrint>;

最后一个显式实例化的结果是使用 NoPrint object 并调用方法 noPrint() 生成用于扩展模板的代码。

在单独的 test.cpp 文件中,我的测试程序有 driver。

#include "foo.hpp"
#include "hasprint.hpp"
#include "alsohasprint.hpp"
#include "noprint.hpp"

int
main (int argc, char** argv)
{
  HasPrint h1;
  HasPrint h2;
  AlsoHasPrint a1;
  NoPrint n1;
  foo<HasPrint, HasPrint> f1 (h1, h2);
  foo<HasPrint, AlsoHasPrint> f2 (h1, a1);
  foo<AlsoHasPrint, AlsoHasPrint> f3 (a1, a1);
  foo<NoPrint, HasPrint> f4 (n1, h1);


  f1.print();
  f2.print();
  f3.print();
  f4.print();
}

这在 gcc 4.8.3 和 clang 3.2 上都按预期工作。结果是:

T1 is: HasPrint
T2 is: HasPrint

T1 is: HasPrint
T2 is: AlsoHasPrint

T1 is: AlsoHasPrint
T2 is: AlsoHasPrint

NoPrint is: NoPrint
T2 is: HasPrint

这使 foo.hpp 的 header 保持良好和干净,代价是需要对我使用模板的每组类型使用显式实例化,如果其中之一需要部分特化参数类型不符合接口。

考虑到我在测试 driver 中使用类型 Foo< NoPrint, HasPrint >,但没有通知编译器存在特化,这是 "well-formed" 还是我只是走运?

谢谢

根据 [temp.class.spec]:

,您的代码格式错误,不需要诊断

A partial specialization shall be declared before the first use of a class template specialization that would make use of the partial specialization as the result of an implicit or explicit instantiation in every translation unit in which such a use occurs; no diagnostic is required.

也就是说,对于这种情况,您根本不需要部分专业化,只需简单地转发您的成员即可:

// from the primary
void print() {
    print("T1", d_t1);
    print("T2", d_t2);
}

其中:

// has print()
template <typename T,
          typename = std::enable_if_t<has_print<T>::value>>
void print(const char* name, const T& val) {
    std::cout << name << " is: ";
    val.print();
    std::cout << std::endl;
}

// has noPrint()
template <typename T,
          typename = std::enable_if_t<has_noprint<T>::value>>
void print(const char* /* unused */, const T& val) {
    std::cout << "NoPrint: ";
    val.noPrint();
    std::cout << std::endl;
}

我将把这些类型特征的实现留作 reader 的练习。有关如何编写此类内容的几种不同方式的指南,请参阅 , with solutions involving , , , , and