为两个相似的类定义一次成员函数?
Define member functions once for two similar classes?
是否可以避免为下面的结构A、B定义相同的函数两次?它们的成员命名完全相同,但是 v0, v1 ... vN 成员在两个结构 A 和 B。如果它有助于成员 v* 都派生自相同的结构 V.
非平凡函数(即赋值 =)可以在结构外重用模板化复制函数,如下所示,但如果它在结构内定义一次,则为 preferred/cleaner。
有没有一种干净的方法可以将 A 和 B 定义模板化为一个定义?
template <class T1, class T2>
void copy(T1& to, T2& from)
{
to.v0 = from.v0;
to.v1 = from.v1;
to.type = from.type;
}
enum class E { TYPE_0, TYPE_1 };
struct B;
struct A
{
C0<float> v0;
C1<int> v1;
E type;
A& operator = (const B& t)
{
copy(*this, t);
return *this;
}
string strType() { return string(type); }
};
struct B
{
D0<float> v0;
D1<int> v1;
E type;
B& operator = (const A& t)
{
copy(*this, t);
return *this;
}
string strType() { return string(type); }
}
您可以为常用功能保留一个 common
基本模板 class,并从中继承 class A
和 B
。
然而,复制赋值运算符是一个特殊的成员函数,我不认为可以在基 class 中为不同类型 return 模板。因此,您需要为每个 class.
提供
意思是,你可以做到
enum class E { TYPE_0, TYPE_1 };
struct B;
template<typename ClassA, typename ClassB> struct Common
{
template<E type>
std::string strType() { return std::to_string(static_cast<int>(type)); }
// need to cast the type to int before you convert std::to_string
// other common-member-functions
};
struct A : public Common<A, B>
{
C0 v0;
C1 v1;
E type;
// ... A& operator = (const B& t)
// bring the common functionalities to A
using Common<A, B>::strType;
// ... other member-functions
};
struct B : public Common<B, A>
{
D0 v0;
D1 v1;
E type;
// ... B& operator = (const A& t)
// bring the common functionalities to A
using Common<B, A>::strType;
// ... other member-functions
};
但是,A
、B
这两个结构似乎只与两个成员不同(即 C0
、C1
和 D0
、D1
),通过制作 class-template 将两个 class 合并为一个,也可以选择:
示例代码如下:
#include <iostream>
#include <vector>
#include <string>
enum class E { TYPE_0, TYPE_1 };
template<typename T1, typename T2>
struct AandB
{
T1 v0;
T2 v1;
E type;
AandB() : type{ E::TYPE_0 } {}
AandB& operator= (const AandB& rhs) // one operator =
{
v0 = rhs.v0;
v1 = rhs.v1;
type = rhs.type;
return *this;
}
std::string strType() const { return std::to_string(static_cast<int>(type)); }
};
int main()
{
using C0 = std::vector<float>;
using C1 = std::vector<int>;
AandB<C0, C1> obj;
std::cout << obj.strType() ; // Prints: 0
}
这正是称为 CRTP(奇怪的重复模板模式)的模式的用例。
我们的想法是定义一个基础 class 模板,它将派生的 class 作为模板参数。然后您可以安全地将基 class 中的 this
转换为派生 class 并访问基 class.[=13= 中的派生 class 成员]
这也适用于复制赋值运算符。
#include <string>
#include <iostream>
template<typename Derived>
struct CRTPBase {
Derived& self() { return static_cast<Derived&>(*this); }
const Derived& self() const { return static_cast<const Derived&>(*this); }
std::string strType() { return self().type;}
template<class OtherDerived>
Derived& operator=(const CRTPBase<OtherDerived>& other) {
self().type = other.self().type;
return self();
}
};
struct A : public CRTPBase<A>
{
using Base = CRTPBase<A>;
std::string type = "A";
using Base::operator=;
};
struct B : public CRTPBase<B>
{
using Base = CRTPBase<B>;
std::string type = "B";
using Base::operator=;
};
int main() {
A a;
B b;
std::cout << a.strType() << std::endl; // Prints: A
std::cout << b.strType() << std::endl; // Prints: B
a = b;
std::cout << a.strType() << std::endl; // Now prints: B
}
实例here.
是否可以避免为下面的结构A、B定义相同的函数两次?它们的成员命名完全相同,但是 v0, v1 ... vN 成员在两个结构 A 和 B。如果它有助于成员 v* 都派生自相同的结构 V.
非平凡函数(即赋值 =)可以在结构外重用模板化复制函数,如下所示,但如果它在结构内定义一次,则为 preferred/cleaner。
有没有一种干净的方法可以将 A 和 B 定义模板化为一个定义?
template <class T1, class T2>
void copy(T1& to, T2& from)
{
to.v0 = from.v0;
to.v1 = from.v1;
to.type = from.type;
}
enum class E { TYPE_0, TYPE_1 };
struct B;
struct A
{
C0<float> v0;
C1<int> v1;
E type;
A& operator = (const B& t)
{
copy(*this, t);
return *this;
}
string strType() { return string(type); }
};
struct B
{
D0<float> v0;
D1<int> v1;
E type;
B& operator = (const A& t)
{
copy(*this, t);
return *this;
}
string strType() { return string(type); }
}
您可以为常用功能保留一个 common
基本模板 class,并从中继承 class A
和 B
。
然而,复制赋值运算符是一个特殊的成员函数,我不认为可以在基 class 中为不同类型 return 模板。因此,您需要为每个 class.
提供意思是,你可以做到
enum class E { TYPE_0, TYPE_1 };
struct B;
template<typename ClassA, typename ClassB> struct Common
{
template<E type>
std::string strType() { return std::to_string(static_cast<int>(type)); }
// need to cast the type to int before you convert std::to_string
// other common-member-functions
};
struct A : public Common<A, B>
{
C0 v0;
C1 v1;
E type;
// ... A& operator = (const B& t)
// bring the common functionalities to A
using Common<A, B>::strType;
// ... other member-functions
};
struct B : public Common<B, A>
{
D0 v0;
D1 v1;
E type;
// ... B& operator = (const A& t)
// bring the common functionalities to A
using Common<B, A>::strType;
// ... other member-functions
};
但是,A
、B
这两个结构似乎只与两个成员不同(即 C0
、C1
和 D0
、D1
),通过制作 class-template 将两个 class 合并为一个,也可以选择:
示例代码如下:
#include <iostream>
#include <vector>
#include <string>
enum class E { TYPE_0, TYPE_1 };
template<typename T1, typename T2>
struct AandB
{
T1 v0;
T2 v1;
E type;
AandB() : type{ E::TYPE_0 } {}
AandB& operator= (const AandB& rhs) // one operator =
{
v0 = rhs.v0;
v1 = rhs.v1;
type = rhs.type;
return *this;
}
std::string strType() const { return std::to_string(static_cast<int>(type)); }
};
int main()
{
using C0 = std::vector<float>;
using C1 = std::vector<int>;
AandB<C0, C1> obj;
std::cout << obj.strType() ; // Prints: 0
}
这正是称为 CRTP(奇怪的重复模板模式)的模式的用例。
我们的想法是定义一个基础 class 模板,它将派生的 class 作为模板参数。然后您可以安全地将基 class 中的 this
转换为派生 class 并访问基 class.[=13= 中的派生 class 成员]
这也适用于复制赋值运算符。
#include <string>
#include <iostream>
template<typename Derived>
struct CRTPBase {
Derived& self() { return static_cast<Derived&>(*this); }
const Derived& self() const { return static_cast<const Derived&>(*this); }
std::string strType() { return self().type;}
template<class OtherDerived>
Derived& operator=(const CRTPBase<OtherDerived>& other) {
self().type = other.self().type;
return self();
}
};
struct A : public CRTPBase<A>
{
using Base = CRTPBase<A>;
std::string type = "A";
using Base::operator=;
};
struct B : public CRTPBase<B>
{
using Base = CRTPBase<B>;
std::string type = "B";
using Base::operator=;
};
int main() {
A a;
B b;
std::cout << a.strType() << std::endl; // Prints: A
std::cout << b.strType() << std::endl; // Prints: B
a = b;
std::cout << a.strType() << std::endl; // Now prints: B
}
实例here.