给定一个普通的数据C++类或由实现的类型组成的结构operator+:
struct VertexData
{
Vec4 vertex;
Vec2 texCoord;
};
Run Code Online (Sandbox Code Playgroud)
是否可以使用模板或其他技巧让C++编译器自动生成operator+添加每个成员,如下所示?
VertexData operator+(VertexData const &a, VertexData const &b)
{
VertexData sum;
sum.vertex = a.vertex + b.vertex;
sum.texCoord = a.texCoord + b.texCoord;
return sum;
}
Run Code Online (Sandbox Code Playgroud)
免责声明这或多或少是"自动的".
我希望你喜欢的模板.)(另外这个需要C++ 11,最显着的,因为它使用的元组和可变参数模板)
另外,我要感谢Rapptz使用索引技巧解决了对元组的递归/迭代.
所以,这是我有两个想法的混合.第一个是我之前在评论中发布的那个.有这种想法的问题是,你只限于把你的所有成员变量的元组,这是非常不方便的.我有另一个想法没有成功,因为它涉及与Adder模板的循环依赖.
所以最后的想法是你继承了加法器模板,假设你有一个静态成员变量(类型为元组),你可以在其中添加指向你想要添加的成员变量的指针.您继承的默认实现创建一个T类型的新变量,迭代参数元组并对它们进行成员明智的添加.
你可以在这里看到它.请注意,您应该能够以与operator +相同的方式添加对tostring(或operator <<)的支持(而不是像我一样手动添加),并且对于来自initializer-list的构造也是如此.
#include <iostream>
#include <string>
#include <type_traits>
#include <tuple>
template<size_t... Ns>
struct indices {};
template<size_t N, size_t... Ns>
struct build_indices : build_indices<N-1, N-1, Ns...> {};
template<size_t... Ns>
struct build_indices<0, Ns...> : indices<Ns...> {};
template<typename T, typename Tuple, size_t... Indices>
void memberWiseSum(T& lhs, T& rhs, T& sum, const Tuple& typeInfo, indices<Indices...>)
{
using expander = int[];
(void)expander{((sum.*std::get<Indices>(typeInfo) = lhs.*std::get<Indices>(typeInfo) + rhs.*std::get<Indices>(typeInfo)), 0)...};
}
template<typename T>
struct Adder
{
T operator+(Adder<T>& rhs)
{
T sum;
memberWiseSum(*static_cast<T*>(this), *static_cast<T*>(&rhs), *static_cast<T*>(&sum), T::typeInfo, build_indices<std::tuple_size<decltype(T::typeInfo)>::value>{});
return sum;
}
};
struct Vec4: public Adder<Vec4>
{
float x,y,z,w;
std::string toString()
{
return "{" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ", " + std::to_string(w) + "}";
}
const static std::tuple<decltype(&Vec4::x), decltype(&Vec4::y), decltype(&Vec4::z), decltype(&Vec4::w)> typeInfo;
};
decltype(Vec4::typeInfo) Vec4::typeInfo(&Vec4::x, &Vec4::y, &Vec4::z, &Vec4::w);
struct Vec2: public Adder<Vec2>
{
float x,y;
std::string toString()
{
return "{" + std::to_string(x) + ", " + std::to_string(y) + "}";
}
const static std::tuple<decltype(&Vec2::x), decltype(&Vec2::y)> typeInfo;
};
decltype(Vec2::typeInfo) Vec2::typeInfo(&Vec2::x, &Vec2::y);
struct VertexData: public Adder<VertexData>
{
Vec4 vertex;
Vec2 texCoord;
std::string toString()
{
return "{" + vertex.toString() + ", " + texCoord.toString() + "}";
}
const static std::tuple<decltype(&VertexData::vertex), decltype(&VertexData::texCoord)> typeInfo;
};
decltype(VertexData::typeInfo) VertexData::typeInfo(&VertexData::vertex, &VertexData::texCoord);
int main()
{
VertexData vd1; vd1.vertex.x = 1; vd1.vertex.y = 2; vd1.vertex.z = 3; vd1.vertex.w = 4;
vd1.texCoord.x = 5; vd1.texCoord.y = 6;
VertexData vd2; vd2.vertex.x = 1; vd2.vertex.y = 2; vd2.vertex.z = 3; vd2.vertex.w = 4;
vd2.texCoord.x = 5; vd2.texCoord.y = 6;
VertexData vd3 = vd1 + vd2;
std::cout << vd3.toString() << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
最后,正如评论和Yakk所提到的,一个真正自动化的解决方案需要一个反射系统(很像C#所具有的),但目前在C++中并不存在.
不,这样做需要(至少)编译时反射,即编写了解类成员变量列表的代码的能力。
C++ 中没有任何这样的构造。有许多建议将编译时反射添加到 C++ 中,它们可能会出现在 C++17 中。
如果您更改了类型,以便数据不存储在成员变量中,而是存储在可以在编译时反映的结构中,那么就可以做到这一点。
作为示例,std::tuple是类型化数据的有序元组。存储在 a 中的数据std::tuple可以被反映,并且通过一些执行您要求的工作的操作符可以编写出可以工作的内容。如果您不将数据存储在成员变量中,而是tuple通过继承或组合将其存储在 a 中,那么您的问题就可以解决。
您还可以编写自己的反射精简版:如果您编写了一个返回 的成员函数std::tie( each member variable in turn ),那么这将为您提供对结构内容的有限反射。编写一个operator+可以对任何提供此类成员函数的类进行操作的方法并不困难。
现在,这涉及编写几乎与编写实际 一样多的代码operator+,但作为奖励,相同的tie函数可以用于其他操作(无论是 it<或==or -or *)。我建议使用 或 CRTP 来标记您想要自动生成哪些操作,然后使用 SFINAE 运算符来完成实际工作(如果您走这条路线)。特征类也可以工作(或者,使用 CRTP 和特征类,其中默认特征类使用 CRTP,但您可以实现自己的特征类)。我对特征类解决方案唯一担心的是操作员缺乏 ADL:我怀疑您必须手动将它们拉入?
最后,通常认为将其实现operator+=为成员函数,然后编写一个operator+使用+=.