gpt4 book ai didi

C++ 模板特化冗余减少

转载 作者:行者123 更新时间:2023-11-30 05:24:52 26 4
gpt4 key购买 nike

我想编写自己的 Vector 类模板,还想添加一些特殊化,例如 3D vector 类型,其中可以通过 x/y/z 访问组件。

到目前为止,模板和特化工作正常,但问题是,特化模板需要从基本模板进行大量复制/粘贴才能工作。我想减少它。

这是现在的样子:

template<class T, unsigned int dim>
class Vector;

template<class T, unsigned int dim>
Vector<T, dim> add(Vector<T, dim> const& lhs, Vector<T, dim> const& rhs)
{
Vector<T, dim> tmp;
for (unsigned int i = 0; i < dim; ++i)
{
tmp[i] = lhs[i] + rhs[i];
}

return tmp;
}

template<class T, unsigned int dim, class S>
Vector<T, dim> add(Vector<T, dim> const& lhs, S const& rhs)
{
Vector<T, dim> tmp;
for (unsigned int i = 0; i < dim; ++i)
{
tmp[i] = lhs[i] + rhs;
}

return tmp;
}

template<class T, unsigned int dim>
Vector<T, dim> operator+(Vector<T, dim> const& lhs, Vector<T, dim> const& rhs)
{
return vectors::add(lhs, rhs);
}

template<class T, unsigned int dim, class S>
Vector<T, dim> operator+(Vector<T, dim> const& lhs, S const& rhs)
{
return vectors::add(lhs, rhs);
}

template<class T, unsigned int dim>
class Vector
{
//...
protected:
T values[dim] __attribute((aligned(16)));
public:
template<class R, unsigned int fdim>
friend Vector<R, fdim> operator+(Vector<R, fdim> const& lhs, Vector<R, fdim> const& rhs);
template<class R, unsigned int fdim, class S>
friend Vector<R, fdim> operator+(Vector<R, fdim> const& lhs, S const& rhs);
template<class R, unsigned int fdim, class S>
friend Vector<R, fdim> operator+(S const& lhs, Vector<R, fdim> const& rhs);
//...
//constructors, etc.
};

template<class T>
class Vector<T, 3>
{
//...
protected:
T values[3] __attribute((aligned(16)));
public:
T& x = values[0];
T& y = values[1];
T& z = values[2];

//lots of copy-pasta :(
template<class R, unsigned int fdim>
friend Vector<R, fdim> operator+(Vector<R, fdim> const& lhs, Vector<R, fdim> const& rhs);
template<class R, unsigned int fdim, class S>
friend Vector<R, fdim> operator+(Vector<R, fdim> const& lhs, S const& rhs);
template<class R, unsigned int fdim, class S>
friend Vector<R, fdim> operator+(S const& lhs, Vector<R, fdim> const& rhs);
//...
//constructors, etc.
};

现在我认为简单的解决方案是简单地定义 Vector3D作为 Vector 的子类模板,像这样:

template<class T>
class Vector3D: public Vector<T, 3>
{
//...
public:
T& x = values[0];
T& y = values[1];
T& z = values[2];

//no copy-pasta :)
//...
//constructors, etc.
};

由于歧义,这根本行不通:

ambiguous overload for ‘operator+’ (operand types are ‘const vec3f {aka const math::vectors::Vector3D<float>}’ and ‘math::vectors::vec3f {aka math::vectors::Vector3D<float>}’)
../main.cpp:84:16: note: candidates are:
In file included from ../main.cpp:10:0:
../include/vector.hpp:720:16: note: math::vectors::Vector<T, dim> math::vectors::operator+(const math::vectors::Vector<T, dim>&, const math::vectors::Vector<T, dim>&) [with T = float; unsigned int dim = 3u]
Vector<T, dim> operator+(Vector<T, dim> const& lhs, Vector<T, dim> const& rhs)
^
../include/vector.hpp:726:16: note: math::vectors::Vector<T, dim> math::vectors::operator+(const math::vectors::Vector<T, dim>&, const S&) [with T = float; unsigned int dim = 3u; S = math::vectors::Vector3D<float>]
Vector<T, dim> operator+(Vector<T, dim> const& lhs, S const& rhs)
^
../include/vector.hpp:732:16: note: math::vectors::Vector<T, dim> math::vectors::operator+(const S&, const math::vectors::Vector<T, dim>&) [with T = float; unsigned int dim = 3u; S = math::vectors::Vector3D<float>]
Vector<T, dim> operator+(S const& lhs, Vector<T, dim> const& rhs)

所以看起来模板替换失败了,因为 S 也可以替换为新的 Vector3D 类,而它应该只处理标量。

所以我试图通过为标量编写一个小的包装类来摆脱这个问题,如下所示:

template<class T>
class ScalarType
{
public:
T value;
ScalarType() :
value(0)
{

}

ScalarType(T const& _v) :
value(_v)
{

}

ScalarType(ScalarType<T> const& rhs) :
value(rhs.value)
{

}

operator T&()
{
return value;
}

operator T() const
{
return value;
}
};

并替换 S const& (l|r)hs 的所有实例与 ScalarType<S> const& (l|r)hs .

这让两边都有 Vectors 的运算符再次工作,但是应该处理 Vector-Scalar 运算的运算符仍然失败。

这次是因为标量值必须显式为 ScalarType 类型,因为隐式转换到它不适用于模板替换。

那么,有什么方法可以让它正常工作,还是我必须坚持使用复制粘贴代码?

最佳答案

此处使用部分模板特化和 CRTP 完成。

maybe_has_z<Container, N>是一个翻译 Container::z() 的类进入Container::operator[](2) , 但前提是 Container::size() >= 3

#include <array>
#include <iostream>
#include <algorithm>

//
// some boilerplate - note the different indecies
//

// define some concepts

template<class Container, std::size_t N, typename= void>
struct maybe_has_x{};

template<class Container, std::size_t N, typename = void>
struct maybe_has_y{};

template<class Container, std::size_t N, typename = void>
struct maybe_has_z{};

// specialise the concepts into (sometimes) concrete accessors

template<class Container, std::size_t N>
struct maybe_has_x<Container, N, std::enable_if_t<(N > 0)>>
{
auto& x() const { return static_cast<const Container&>(*this)[0]; }
auto& x() { return static_cast<Container&>(*this)[0]; }
};

template<class Container, std::size_t N>
struct maybe_has_y<Container, N, std::enable_if_t<(N > 1)>>
{
auto& y() const { return static_cast<const Container&>(*this)[1]; }
auto& y() { return static_cast<Container&>(*this)[1]; }
};

template<class Container, std::size_t N>
struct maybe_has_z<Container, N, std::enable_if_t<(N > 2)>>
{
auto& z() const { return static_cast<const Container&>(*this)[2]; }
auto& z() { return static_cast<Container&>(*this)[2]; }
};

// define our vector type

template<class T, std::size_t N>
struct Vector
: std::array<T, N>
, maybe_has_x<Vector<T, N>, N> // include the maybe_ concepts
, maybe_has_y<Vector<T, N>, N>
, maybe_has_z<Vector<T, N>, N>
{
private:
using inherited = std::array<T, N>;
public:
Vector() : inherited {} {};
Vector(std::initializer_list<T> il)
: inherited { }
{
std::copy_n(il.begin(), std::min(il.size(), this->size()), std::begin(*this));
}
Vector(const inherited& rhs) : inherited(rhs) {}

public:
using value_type = typename inherited::value_type;

// offer arithmetic unary functions in class (example +=)
// note that this allows us to add integers to a vector of doubles
template<class Other, std::enable_if_t<std::is_convertible<value_type, Other>::value> * = nullptr>
Vector& operator+=(const Vector<Other, N>&rhs) {
auto lfirst = std::begin(*this);
auto rfirst = std::begin(rhs);
auto lend = std::end(*this);
while (lfirst != lend) {
*lfirst += *rfirst;
++lfirst;
++rfirst;
}
return *this;
}

};

// offer binary arithmetic as free functions

template<class T, std::size_t N, class Other>
Vector<T, N> operator+(Vector<T, N> lhs, const Vector<Other, N>& rhs) {
lhs += rhs;
return lhs;
}

// offer some streaming capability

template<class T, std::size_t N>
std::ostream& operator<<(std::ostream& os, const Vector<T, N>& rhs) {
auto sep = "";
os << '[';
for (auto& x : rhs) {
os << sep << x;
sep = ", ";
}
return os << ']';
}

// test

int main()
{
auto a = Vector<double, 3> { 2.1, 1.2, 3.3 };
auto b = a + a + Vector<int, 3> { 1, 1, 1 };
std::cout << a << std::endl;
std::cout << b << std::endl;

std::cout << a.x() << ", " << a.y() << ", " << a.z() << std::endl;

auto c = Vector<double, 2> { 4.4, 5.5 };
std::cout << c << std::endl;

std::cout << c.x() << std::endl;
std::cout << c.y() << std::endl;
// won't compile
// std::cout << c.z() << std::endl;
}

预期输出:

[2.1, 1.2, 3.3]
[5.2, 3.4, 7.6]
2.1, 1.2, 3.3
[4.4, 5.5]
4.4
5.5

关于C++ 模板特化冗余减少,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38541405/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com