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 。
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 的练习。有关如何编写此类内容的几种不同方式的指南,请参阅