首页 文章

cpp文件中的部分特化是不是“格式良好”

提问于
浏览
2

这是[问题]的后续行动:No generated code for explicitly specialized template even with explicit instantiation .

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

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

#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文件中的部分特化来处理它 . 此模板不适用于真正的通用用法,它只需要使用我预先知道的少量类型 .

例如,我可能有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'类具有print()方法,而'NoPrint'类具有noPrint()方法 . 在这些类的主体中,print / noPrint方法只是打印类的名称 .

为了处理使用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对象生成代码以扩展模板并调用方法noPrint() .

在一个单独的test.cpp文件中,我有我的测试程序的驱动程序 .

#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的头文保持良好和干净,代价是需要对我使用模板的每组类型使用显式实例化,如果其中一个参数类型不符合接口,则部分特化 .

鉴于我在测试驱动程序中使用了类型为Foo <NoPrint,HasPrint>,而没有通知编译器存在专门化,这是“格式良好”还是我很幸运?

谢谢

1 回答

  • 3

    根据[temp.class.spec],您的代码格式错误,无需诊断:

    在第一次使用类模板特化之前,应声明部分特化,该特殊化将利用部分特化作为在发生这种使用的每个翻译单元中进行隐式或显式实例化的结果;无需诊断 .

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

    // 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;
    }
    

    我会将这些类型特征的实现作为练习留给读者 . 有关如何编写此类内容的几种不同方法的指南,请参阅this question,其中包含涉及std::experimental::is_detectedvoid_toverload resolution with trailing-return-typecan_applyREQUIRES的解决方案 .

相关问题