是否可以避免对独立功能进行重复的通用检查?
Is it possible to avoid repetitive generic checks for free standing functions?
我喜欢独立函数,因为我可以像 v1.dot(v2)
或 dot(v1,v2)
那样调用点积,但它们需要更多的努力来编写。
例如
float dot(Vec)(in Vec v1, in Vec v2)
if(isVector!(Vec) && ...){
....
}
我也有很多不同的功能,只能在特定维度上工作,而且检查都非常重复。
要是能写点长篇就好了
template(Vec)
if(isVector!Vec && ...){
//functions here
float dot(in Vec v1, in Vec v2){..}
float length(in Vec v1, in Vec v2){..}
float lengthSquared(in Vec v1, in Vec v2){..}
}
我想调用它们就像每个函数都有自己的模板一样,就像 dot(v1,v2)
。这样的事情是可能的还是我应该坚持使用方法?
我不太确定我是否得到您的要求。是否将某些东西创建为方法/成员函数取决于您,并且与函数是否被模板化实际上没有太大关系。如果期望一个函数采用特定类型,那么无论它是自由函数还是成员函数,都可以声明它采用该类型。如果一个函数应该与多种类型一起工作,那么它需要被模板化,在这种情况下,如果其中一个参数总是应该是一个特定的类型,那么它可以是该类型的成员函数,但除此之外,它必须是自由函数才能通用。
当你有一个模板化类型时,你想要编写一个只接受该类型的免费函数,这会让人感到奇怪。您被迫模板化自由函数并测试它给出的类型是否是该类型的实例化,因此模板约束开始变得更丑陋和更复杂,而成员函数本来可以是干净的。而不是像
template Vector(T)
if(...)
{
...
float dot(Vector v1, Vector v2) {...}
...
}
你最终得到
float dot(V)(V v1, V v2)
if(isInstanceOf!(Vector, V))
{...}
这是可行的,但它增加了不必要的样板文件,唯一的好处是能够执行 dot(v1, v2)
而不是 v1.dot(v2)
。我认为绝大多数 D 程序员会认为那只是丑陋和不必要的,而且他们通常会想要 v1.dot(v2)
无论如何。使用 UFCS 使得几乎所有代码都是 a.foo(b)
,无论 foo
是自由函数还是成员函数。你是我第一个听说试图让一切都变成foo(a, b)
而不是a.foo(b)
,一般情况下是行不通的,因为很多函数都是成员函数,不能调用作为自由函数,而 all 函数可以像成员函数一样被调用——这就是通用代码应该倾向于使用 UFCS 的原因。它只是简单地使代码适用于更多类型,并允许类型提供自由函数的特化作为成员函数。
但是,如果出于某种原因,您拥有模板化的函数,这些函数通常是您需要(或真正希望)成为自由函数的成员函数,并且您想减少冗余模板约束,那么这样做的方法就是创造自己的特色。例如
template myCond(T)
{
enum myCond = conditionAboutT1 && conditionAboutT2 && conditionAboutT3;
}
那么您的函数主机就不必重复它们的那部分约束。例如
float dot(V)(V v1, V v2)
if(myCond!V && dotSpecificCondAboutV)
{...}
float lengthSquared(V)(V v1, V v2)
if(myCond!V && lengthSquaredSpecificCondAboutV)
{...}
但是,尝试将一组函数分组到一个模板中以便它们可以共享一个约束实际上只有在它们是成员函数时才有效。如果你有类似
template Vec(V)
if(...)
{
float dot(V v1, V v2) {...}
float lengthSquared(V v1, V v2) {...}
}
然后你将不得不在使用函数的任何地方使用模板名称,并且你失去了 IFTI(隐式函数模板实例化),这就是它的原因,所以你不必明确使用与它们一起使用的类型实例化模板化函数。所以,而不是得到像
这样的东西
auto result = dot(v1, v2);
或
auto result = v1.dot(v2);
你最终会得到类似
的东西
auto result = Vec!Vector(v1, v2);
对于非模板类型或
auto result = Vec!(Vector!float)(v1, v2);
对于模板化类型。
最终,您确实需要让这些函数成为成员函数,或者将它们全部单独列出并带有它们自己的模板约束 - 但无论如何,声明您自己的特征以测试常见的东西会减少模板的长度约束,使它们更容易理解和维护。
我喜欢独立函数,因为我可以像 v1.dot(v2)
或 dot(v1,v2)
那样调用点积,但它们需要更多的努力来编写。
例如
float dot(Vec)(in Vec v1, in Vec v2)
if(isVector!(Vec) && ...){
....
}
我也有很多不同的功能,只能在特定维度上工作,而且检查都非常重复。
要是能写点长篇就好了
template(Vec)
if(isVector!Vec && ...){
//functions here
float dot(in Vec v1, in Vec v2){..}
float length(in Vec v1, in Vec v2){..}
float lengthSquared(in Vec v1, in Vec v2){..}
}
我想调用它们就像每个函数都有自己的模板一样,就像 dot(v1,v2)
。这样的事情是可能的还是我应该坚持使用方法?
我不太确定我是否得到您的要求。是否将某些东西创建为方法/成员函数取决于您,并且与函数是否被模板化实际上没有太大关系。如果期望一个函数采用特定类型,那么无论它是自由函数还是成员函数,都可以声明它采用该类型。如果一个函数应该与多种类型一起工作,那么它需要被模板化,在这种情况下,如果其中一个参数总是应该是一个特定的类型,那么它可以是该类型的成员函数,但除此之外,它必须是自由函数才能通用。
当你有一个模板化类型时,你想要编写一个只接受该类型的免费函数,这会让人感到奇怪。您被迫模板化自由函数并测试它给出的类型是否是该类型的实例化,因此模板约束开始变得更丑陋和更复杂,而成员函数本来可以是干净的。而不是像
template Vector(T)
if(...)
{
...
float dot(Vector v1, Vector v2) {...}
...
}
你最终得到
float dot(V)(V v1, V v2)
if(isInstanceOf!(Vector, V))
{...}
这是可行的,但它增加了不必要的样板文件,唯一的好处是能够执行 dot(v1, v2)
而不是 v1.dot(v2)
。我认为绝大多数 D 程序员会认为那只是丑陋和不必要的,而且他们通常会想要 v1.dot(v2)
无论如何。使用 UFCS 使得几乎所有代码都是 a.foo(b)
,无论 foo
是自由函数还是成员函数。你是我第一个听说试图让一切都变成foo(a, b)
而不是a.foo(b)
,一般情况下是行不通的,因为很多函数都是成员函数,不能调用作为自由函数,而 all 函数可以像成员函数一样被调用——这就是通用代码应该倾向于使用 UFCS 的原因。它只是简单地使代码适用于更多类型,并允许类型提供自由函数的特化作为成员函数。
但是,如果出于某种原因,您拥有模板化的函数,这些函数通常是您需要(或真正希望)成为自由函数的成员函数,并且您想减少冗余模板约束,那么这样做的方法就是创造自己的特色。例如
template myCond(T)
{
enum myCond = conditionAboutT1 && conditionAboutT2 && conditionAboutT3;
}
那么您的函数主机就不必重复它们的那部分约束。例如
float dot(V)(V v1, V v2)
if(myCond!V && dotSpecificCondAboutV)
{...}
float lengthSquared(V)(V v1, V v2)
if(myCond!V && lengthSquaredSpecificCondAboutV)
{...}
但是,尝试将一组函数分组到一个模板中以便它们可以共享一个约束实际上只有在它们是成员函数时才有效。如果你有类似
template Vec(V)
if(...)
{
float dot(V v1, V v2) {...}
float lengthSquared(V v1, V v2) {...}
}
然后你将不得不在使用函数的任何地方使用模板名称,并且你失去了 IFTI(隐式函数模板实例化),这就是它的原因,所以你不必明确使用与它们一起使用的类型实例化模板化函数。所以,而不是得到像
这样的东西auto result = dot(v1, v2);
或
auto result = v1.dot(v2);
你最终会得到类似
的东西auto result = Vec!Vector(v1, v2);
对于非模板类型或
auto result = Vec!(Vector!float)(v1, v2);
对于模板化类型。
最终,您确实需要让这些函数成为成员函数,或者将它们全部单独列出并带有它们自己的模板约束 - 但无论如何,声明您自己的特征以测试常见的东西会减少模板的长度约束,使它们更容易理解和维护。