- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我们知道,函数模板不能在 C++ 中部分特化。当您在概念上试图实现这一点时,您可以使用两种可能的解决方案。其中之一是使用带有静态函数的结构,可以选择用模板函数包装,如下所示:
template <class T, class U>
struct BarHelper
{
static void BarHelp(T t, const U& u)
{
std::cerr << "bar general\n";
}
};
template <class T>
struct BarHelper<T, double>
{
static void BarHelp(T t, const double& u)
{
std::cerr << "bar specialized\n";
}
};
template <class T, class U>
void bar(T t, const U& u)
{
BarHelper<T, U>::BarHelp(t, u);
};
bar
这里是可选的,如果你愿意,你可以直接使用结构的静态成员(尽管你必须显式指定所有参数)。
另一种方法只是重载函数模板:
template <class T, class U>
void func(T t, const U& u)
{
std::cerr << "func general\n";
}
template <class T>
void func(T t, const double& u)
{
std::cerr << "func specialized\n";
}
对我来说,第二种方法似乎更可取。对于初学者来说,它不那么冗长,并且在意图方面更加清晰(我们正在编写函数,所以让我们使用函数而不是毫无意义的包装结构)。此外,您可以使用一些很好的技巧来控制重载分辨率。例如,您可以在继承层次结构中使用非模板化的“标记”参数,并使用隐式转换来控制函数的优先级。每当您在重载中具体指定类型时,您还可以获得隐式转换,如果您不喜欢这种行为,您可以在重载上使用 enable_if 来防止它(让您回到与结构相同的位置)。
是否有理由偏爱部分特化的结构?这些原因有多普遍? IE。哪个应该是你的“默认”?如果您:a) 计划自己实现所有特化,而不是 b) 将其用作自定义点,用户可以在其中注入(inject)自己的行为,这会有所不同吗?
Herb Sutter 有一篇关于避免函数模板特化的著名博文。在其中,他还建议(接近尾声)更喜欢部分专用的结构而不是重载的函数模板,但他似乎没有给出任何具体原因:http://www.gotw.ca/publications/mill17.htm .
Moral #2: If you're writing a function base template, prefer to write it as a single function template that should never be specialized or overloaded
(强调)。
最佳答案
让我们首先列出创建同一模板方法的多个变体的选项:
Template function specialization: is NOT an option as template functions cannot be partially specialized. (See SO threads on that here, here and here).
简单的重载:这可以工作,正如问题提到和演示的那样。
但是,它并不总是有效,我们将在下面看到。
使用仿函数类部分特化:这是没有模板函数特化的直接替代方案。
使用<i>std::enable_if</i>
和模板函数重载:当简单的模板重载不起作用时可以选择这种方法,见下文。
EDIT: adding @Nir option 4
--- END of EDIT ---
当从调用中推导出模板参数时,该问题提出了模板函数重载工作正常的情况。但是,如果调用模板函数是直接提供模板参数,并且需要根据模板参数的关系或条件来匹配实现,则重载将无法再提供帮助。
template <typename T, T val1, T val2>
void isSame1() {
cout << "val1: " << val1 << ", val2: " << val2 << " are "
<< (val1==val2?" ":"NOT ") << "the same" << endl;
}
虽然 val1 和 val2 在编译时是已知的,但无法部分特化我们在编译时知道它们相同的情况。函数重载在这种情况下没有帮助,对于两个非类型模板参数具有相同值的情况没有重载。
通过类部分特化,我们可以做到:
template <typename T, T val1, T val2>
struct IsSameHelper {
static void isSame() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
};
// partial specialization
template <typename T, T val>
struct IsSameHelper<T, val, val> {
static void isSame() {
cout << "val1: " << val << ", val2: " << val << " are the same" << endl;
}
};
template <typename T, T val1, T val2>
void isSame2() {
IsSameHelper<T, val1, val2>::isSame();
}
或者,我们可以使用 <i>std::enable_if</i>
:
template<typename T, T val1, T val2>
struct is_same_value : std::false_type {};
template<typename T, T val>
struct is_same_value<T, val, val> : std::true_type {};
template <typename T, T val1, T val2>
typename std::enable_if<!is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
template <typename T, T val1, T val2>
typename std::enable_if<is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are the same" << endl;
}
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame1<int, 3, 4>();
isSame1<int, 3, 3>();
isSame1<int*, &global1, &global1>();
isSame1<int*, &global1, &global2>();
isSame2<int, 3, 4>();
isSame2<int, 3, 3>();
isSame2<int*, &global1, &global1>();
isSame2<int*, &global1, &global2>();
isSame3<int, 3, 4>();
isSame3<int, 3, 3>();
isSame3<int*, &global1, &global1>();
isSame3<int*, &global1, &global2>();
}
EDIT: adding @Nir option 4
template <class T, T v> struct foo{
static constexpr T val = v;
};
// in a .cpp
template <class T, T v>
constexpr T foo<T, v>::val; // required for non-integral / non-enum types
template <class T, T v1, T v2> void isSame4(foo<T, v1> f1, foo<T, v2> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are NOT the same" << endl;
}
template <class T, T v> void isSame4(foo<T, v> f1, foo<T, v> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are the same" << endl;
}
此选项的主要内容如下所示:
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame4(foo<int, 4>(), foo<int, 3>());
isSame4(foo<int, 3>(), foo<int, 3>());
isSame4(foo<int*, &global1>(), foo<int*, &global1>());
isSame4(foo<int*, &global1>(), foo<int*, &global2>());
}
我认为选项 4 的语法没有任何优势。但可以不这样想……
注意选项 4 中需要 .cpp 文件,用于声明 T foo::val
,在所有其他选项中,所有内容都适用于 .h 文件。
--- END of EDIT ---
在我们可以获得编译时间分辨率的情况下,基于模板元编程,需要部分特化。这可以通过类部分特化或使用 enable_if 来实现(这反过来又需要它自己的类部分特化来满足其条件)。
关于c++ - 部分特化结构与重载函数模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37265188/
假设我有一个类,我在其中重载了运算符 == : Class A { ... public: bool operator== (const A &rhs) const; ... };
我知道你不应该使用 std::find(some_map.begin(), some_map.end()) 或 std::lower_bound,因为它会采用线性时间而不是 some_map.lowe
我正在尝试在 Haskell 中定义 Vector3 数据类型,并允许在其上使用 (+) 运算符。我尝试了以下方法: data Vector3 = Vector3 Double Double Doub
我已经为我的类图将运算符重载为“-”。它的用途并不完全直观(糟糕的编码 - 我知道)但是如果我做 graph3 = graph2-graph1 那么图 3 是应该只接收图 2 和图 1 中的那些顶点。
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Operator overloading 我想重载 以按字母顺序排列字符串,但我不确定该怎么做。 如何再次
下面的代码给我一个编译错误。谁能告诉我为什么? class mytype { public: int value; mytype(int a) { value = a;
这有什么问题吗? class Vec2 attr_accessor :x, :y # ... def += (v) @x += v.x @y += v.y retu
是否可以重载 [] 运算符两次?允许这样的事情:function[3][3](就像在二维数组中一样)。 如果可能的话,我想看看一些示例代码。 最佳答案 您可以重载 operator[] 以返回一个对象
我的团队目前正在与 Lua 合作,创建一个 android 游戏。我们遇到的一件事是表面上无法创建重载构造函数。 我习惯于使用默认值设置一个对象,然后在需要时使其过载。 前任: apples() {
我有一个网页,在某个时候显示一个导航栏,它只不过是一个 a 元素的列表 (ul)。所述 a 元素的大多数样式规则都是通用的。唯一应该改变的部分是要显示的图像,可以从列表中每个 li 元素的 id 标签
我对使用/重载“范围步长”运算符(.. ..)很感兴趣,但我终其一生都无法了解如何使用它。 在文档中它说 // Usage: start .. step .. finish 但是在 F# shell
Java 11(可能无关紧要): public static String toString(Object obj) { return ReflectionToStringBuilder.to
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我无法理解以下代码(针对行号进行注释) class Base { void m1(Object o) { } void m2(String o) { } } publi
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
class myclass{ //definitions here }; myclass e; int myarray[10]; /* Do something... */ e = myarray;
为什么不能将下标运算符(operator [])作为 friend 函数重载? 最佳答案 正如Bjarne Stroustrup在D&E book中所说: However, even in the o
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
因此,我有一个问题是我最近尝试重载 namespace Eng { /** * A structure to represent pixels */ typedef
如何重载onResume()以正确的方式工作?我想从 activity 返回到 MainActivity ,我希望在其中具有与应用程序启动后相同的状态。我想使用 recreate() 但它循环了或者类
我是一名优秀的程序员,十分优秀!