ENH: expand VectorSpaceOps to include copy_n/fill_n methods
- similar to what std::copy_n and std::fill_n would do, except with templated loops. This allows compile-time transcribing with loop unrolling. For example, vector vec1 = ..., vec2 = ...; FixedList<scalar, 6> values; VectorSpaceOps<3>::copy_n(vec1.begin(), values.begin()); VectorSpaceOps<3>::copy_n(vec2.begin(), values.begin(3)) // do something with all of these values STYLE: make start index of VectorSpaceOps optional ENH: add clamped begin(int) versions to FixedList as per UList
This commit is contained in:
parent
cf9fa16788
commit
d64682a7af
@ -1,3 +1,3 @@
|
||||
Test-vector.C
|
||||
Test-vector.cxx
|
||||
|
||||
EXE = $(FOAM_USER_APPBIN)/Test-vector
|
||||
|
@ -5,7 +5,7 @@
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2018-2023 OpenCFD Ltd.
|
||||
Copyright (C) 2018-2025 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -32,7 +32,10 @@ Description
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "vectorField.H"
|
||||
#include "boolVector.H"
|
||||
#include "labelVector.H"
|
||||
#include "IOstreams.H"
|
||||
#include "FixedList.H"
|
||||
#include "Random.H"
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
@ -125,6 +128,42 @@ void testNormalise(Field<Type>& fld)
|
||||
}
|
||||
|
||||
|
||||
// Transcribe vectorspace information into a FixedList
|
||||
template<class Type>
|
||||
void testTranscribe(Type& input)
|
||||
{
|
||||
if constexpr
|
||||
(
|
||||
is_vectorspace_v<Type>
|
||||
&& std::is_floating_point_v<typename pTraits_cmptType<Type>::type>
|
||||
)
|
||||
{
|
||||
constexpr auto nCmpts = pTraits_nComponents<Type>::value;
|
||||
using cmpt = typename pTraits_cmptType<Type>::type;
|
||||
|
||||
FixedList<cmpt, nCmpts+1> values;
|
||||
values.back() = 100; // some additional data
|
||||
|
||||
VectorSpaceOps<nCmpts>::copy_n(input.cdata(), values.data());
|
||||
|
||||
Info<< "Transcribed " << input << " => " << values << nl;
|
||||
|
||||
for (auto& val : values)
|
||||
{
|
||||
val *= -1;
|
||||
}
|
||||
|
||||
VectorSpaceOps<nCmpts>::copy_n(values.cdata(), input.data());
|
||||
Info<< " copied back (-1) as " << input
|
||||
<< " from " << values << nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< "Did not transcribe " << input << nl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
// Main program:
|
||||
|
||||
@ -240,6 +279,16 @@ int main(int argc, char *argv[])
|
||||
testNormalise(vfld2);
|
||||
}
|
||||
|
||||
Info<< nl
|
||||
<< "Test transcribing components" << nl;
|
||||
{
|
||||
vector vec1(1.1, 2.2, 3.3);
|
||||
testTranscribe(vec1);
|
||||
|
||||
labelVector vec2(10, 20, 30);
|
||||
testTranscribe(vec2);
|
||||
}
|
||||
|
||||
Info<< "\nEnd\n" << nl;
|
||||
|
||||
return 0;
|
@ -1,3 +1,3 @@
|
||||
Test-vectorTools.C
|
||||
Test-vectorTools.cxx
|
||||
|
||||
EXE = $(FOAM_USER_APPBIN)/Test-vectorTools
|
||||
|
@ -384,6 +384,10 @@ public:
|
||||
//- Return an iterator to end traversing the FixedList
|
||||
inline iterator end() noexcept;
|
||||
|
||||
//- Return iterator at offset i from begin,
|
||||
//- clamped to [0,N] range
|
||||
inline iterator begin(const int i) noexcept;
|
||||
|
||||
|
||||
// Random access iterator (const)
|
||||
|
||||
@ -399,6 +403,14 @@ public:
|
||||
//- Return const_iterator to end traversing the constant FixedList
|
||||
inline const_iterator end() const noexcept;
|
||||
|
||||
//- Return const_iterator at offset i from begin,
|
||||
//- clamped to [0,N] range
|
||||
inline const_iterator cbegin(const int i) const noexcept;
|
||||
|
||||
//- Return const_iterator at offset i from begin,
|
||||
//- clamped to [0,N] range
|
||||
inline const_iterator begin(const int i) const noexcept;
|
||||
|
||||
|
||||
// Reverse iterator (non-const)
|
||||
|
||||
|
@ -499,6 +499,30 @@ Foam::FixedList<T, N>::cbegin() const noexcept
|
||||
}
|
||||
|
||||
|
||||
template<class T, unsigned N>
|
||||
inline typename Foam::FixedList<T, N>::iterator
|
||||
Foam::FixedList<T, N>::begin(const int i) noexcept
|
||||
{
|
||||
return (v_ + (i < 0 ? 0 : int(N) < i ? int(N) : i));
|
||||
}
|
||||
|
||||
|
||||
template<class T, unsigned N>
|
||||
inline typename Foam::FixedList<T, N>::const_iterator
|
||||
Foam::FixedList<T, N>::begin(const int i) const noexcept
|
||||
{
|
||||
return (v_ + (i < 0 ? 0 : int(N) < i ? int(N) : i));
|
||||
}
|
||||
|
||||
|
||||
template<class T, unsigned N>
|
||||
inline typename Foam::FixedList<T, N>::const_iterator
|
||||
Foam::FixedList<T, N>::cbegin(const int i) const noexcept
|
||||
{
|
||||
return (v_ + (i < 0 ? 0 : int(N) < i ? int(N) : i));
|
||||
}
|
||||
|
||||
|
||||
template<class T, unsigned N>
|
||||
inline typename Foam::FixedList<T, N>::iterator
|
||||
Foam::FixedList<T, N>::end() noexcept
|
||||
|
@ -158,7 +158,7 @@ public:
|
||||
// Constructors
|
||||
|
||||
//- Construct initialized to zero
|
||||
inline VectorSpace(const Foam::zero);
|
||||
inline VectorSpace(Foam::zero);
|
||||
|
||||
//- Copy construct
|
||||
inline VectorSpace(const VectorSpace<Form, Cmpt, Ncmpts>& vs);
|
||||
@ -174,10 +174,7 @@ public:
|
||||
// Member Functions
|
||||
|
||||
//- The number of elements in the VectorSpace = Ncmpts.
|
||||
static constexpr direction size() noexcept
|
||||
{
|
||||
return Ncmpts;
|
||||
}
|
||||
static constexpr direction size() noexcept { return Ncmpts; }
|
||||
|
||||
inline const Cmpt& component(const direction) const;
|
||||
inline Cmpt& component(const direction);
|
||||
@ -186,10 +183,10 @@ public:
|
||||
inline void replace(const direction, const Cmpt&);
|
||||
|
||||
//- Return const pointer to the first data element
|
||||
inline const Cmpt* cdata() const noexcept;
|
||||
const Cmpt* cdata() const noexcept { return v_; }
|
||||
|
||||
//- Return pointer to the first data element
|
||||
inline Cmpt* data() noexcept;
|
||||
Cmpt* data() noexcept { return v_; }
|
||||
|
||||
//- Assign all components to given value
|
||||
inline void fill(const Cmpt& s);
|
||||
@ -210,7 +207,7 @@ public:
|
||||
inline void operator+=(const VectorSpace<Form, Cmpt, Ncmpts>&);
|
||||
inline void operator-=(const VectorSpace<Form, Cmpt, Ncmpts>&);
|
||||
|
||||
inline void operator=(const Foam::zero);
|
||||
inline void operator=(Foam::zero);
|
||||
inline void operator*=(const scalar);
|
||||
inline void operator/=(const scalar);
|
||||
|
||||
@ -224,28 +221,25 @@ public:
|
||||
typedef const Cmpt* const_iterator;
|
||||
|
||||
|
||||
// Random access iterator (non-const)
|
||||
// Random access iterators (const and non-const)
|
||||
|
||||
//- Return an iterator to begin of VectorSpace
|
||||
inline iterator begin() noexcept;
|
||||
//- Return an iterator (pointer) to begin of VectorSpace
|
||||
iterator begin() noexcept { return v_; }
|
||||
|
||||
//- Return an iterator to end of VectorSpace
|
||||
inline iterator end() noexcept;
|
||||
//- Return const_iterator (const pointer) to begin of VectorSpace
|
||||
const_iterator begin() const noexcept { return v_; }
|
||||
|
||||
//- Return const_iterator (const pointer) to begin of VectorSpace
|
||||
const_iterator cbegin() const noexcept { return v_; }
|
||||
|
||||
// Random access iterator (const)
|
||||
//- Return an iterator (pointer) to end of VectorSpace
|
||||
iterator end() noexcept { return (v_ + Ncmpts); }
|
||||
|
||||
//- Return const_iterator to begin of VectorSpace
|
||||
inline const_iterator cbegin() const noexcept;
|
||||
//- Return const_iterator (const pointer) to end of VectorSpace
|
||||
const_iterator end() const noexcept { return (v_ + Ncmpts); }
|
||||
|
||||
//- Return const_iterator to end of VectorSpace
|
||||
inline const_iterator cend() const noexcept;
|
||||
|
||||
//- Return const_iterator to begin of VectorSpace
|
||||
inline const_iterator begin() const noexcept;
|
||||
|
||||
//- Return const_iterator to end of VectorSpace
|
||||
inline const_iterator end() const noexcept;
|
||||
//- Return const_iterator (const pointer) to end of VectorSpace
|
||||
const_iterator cend() const noexcept { return (v_ + Ncmpts); }
|
||||
|
||||
|
||||
// IOstream Operators
|
||||
|
@ -35,9 +35,9 @@ License
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace(const Foam::zero)
|
||||
inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace(Foam::zero)
|
||||
{
|
||||
VectorSpaceOps<Ncmpts,0>::eqOpS(*this, Zero, eqOp<Cmpt>());
|
||||
VectorSpaceOps<Ncmpts>::fill_n(this->begin(), Cmpt(Foam::zero{}));
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace
|
||||
const VectorSpace<Form, Cmpt, Ncmpts>& vs
|
||||
)
|
||||
{
|
||||
VectorSpaceOps<Ncmpts,0>::eqOp(*this, vs, eqOp<Cmpt>());
|
||||
VectorSpaceOps<Ncmpts>::copy_n(vs.cbegin(), this->begin());
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace
|
||||
const VectorSpace<Form2, Cmpt2, Ncmpts>& vs
|
||||
)
|
||||
{
|
||||
VectorSpaceOps<Ncmpts,0>::eqOp(*this, vs, eqOp<Cmpt>());
|
||||
VectorSpaceOps<Ncmpts>::copy_n(vs.cbegin(), this->begin());
|
||||
}
|
||||
|
||||
|
||||
@ -163,7 +163,7 @@ inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::replace
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::fill(const Cmpt& s)
|
||||
{
|
||||
VectorSpaceOps<Ncmpts,0>::eqOpS(*this, s, eqOp<Cmpt>());
|
||||
VectorSpaceOps<Ncmpts>::fill_n(this->begin(), s);
|
||||
}
|
||||
|
||||
|
||||
@ -171,7 +171,7 @@ template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline Form Foam::VectorSpace<Form, Cmpt, Ncmpts>::uniform(const Cmpt& s)
|
||||
{
|
||||
Form v;
|
||||
VectorSpaceOps<Ncmpts,0>::eqOpS(v, s, eqOp<Cmpt>());
|
||||
v.fill(s);
|
||||
return v;
|
||||
}
|
||||
|
||||
@ -186,68 +186,6 @@ Foam::VectorSpace<Form, Cmpt, Ncmpts>::block() const
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Iterator * * * * * * * * * * * * * * * * //
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::data() noexcept
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::cdata() const noexcept
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::begin() noexcept
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::end() noexcept
|
||||
{
|
||||
return (v_ + Ncmpts);
|
||||
}
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::cbegin()
|
||||
const noexcept
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::cend()
|
||||
const noexcept
|
||||
{
|
||||
return (v_ + Ncmpts);
|
||||
}
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::begin()
|
||||
const noexcept
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::end()
|
||||
const noexcept
|
||||
{
|
||||
return (v_ + Ncmpts);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
@ -346,7 +284,7 @@ inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator=
|
||||
const VectorSpace<Form, Cmpt, Ncmpts>& vs
|
||||
)
|
||||
{
|
||||
VectorSpaceOps<Ncmpts,0>::eqOp(*this, vs, eqOp<Cmpt>());
|
||||
VectorSpaceOps<Ncmpts>::copy_n(vs.cbegin(), this->begin());
|
||||
}
|
||||
|
||||
|
||||
@ -371,9 +309,9 @@ inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator-=
|
||||
|
||||
|
||||
template<class Form, class Cmpt, Foam::direction Ncmpts>
|
||||
inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator=(const Foam::zero)
|
||||
inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator=(Foam::zero)
|
||||
{
|
||||
VectorSpaceOps<Ncmpts,0>::eqOpS(*this, 0, eqOp<Cmpt>());
|
||||
VectorSpaceOps<Ncmpts>::fill_n(this->begin(), Cmpt(Foam::zero{}));
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,9 +43,40 @@ namespace Foam
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
//- Recursive execution. Terminating at \<N\>, starting at index \<I\>
|
||||
template<direction N, direction I>
|
||||
template<direction N, direction I=0>
|
||||
struct VectorSpaceOps
|
||||
{
|
||||
//- Somewhat equivalent to std::copy_n() but with templated loops.
|
||||
// \param [in] input indexable input data
|
||||
// \param [out] result indexable output data
|
||||
template<class Input, class Output>
|
||||
static inline void copy_n(Input input, Output result)
|
||||
{
|
||||
// if constexpr (I < N)
|
||||
{
|
||||
result[I] = input[I];
|
||||
VectorSpaceOps<N, I+1>::copy_n(input, result);
|
||||
}
|
||||
}
|
||||
|
||||
//- Somewhat equivalent to std::fill_n() but with templated loops
|
||||
// \param [out] result indexable output data
|
||||
// \param val the value to assign for each entry
|
||||
template<class Output, class T>
|
||||
static inline void fill_n(Output result, const T& val)
|
||||
{
|
||||
// if constexpr (I < N)
|
||||
{
|
||||
result[I] = val;
|
||||
VectorSpaceOps<N, I+1>::fill_n(result, val);
|
||||
}
|
||||
}
|
||||
|
||||
//- Apply the binary assignment operation to each vector-space
|
||||
//- component.
|
||||
// \param [in,out] vs vector-space (indexed) data
|
||||
// \param s scalar/component data (non-indexed)
|
||||
// \param eo binary combine/assign operation
|
||||
template<class V, class S, class EqOp>
|
||||
static inline void eqOpS(V& vs, const S& s, EqOp eo)
|
||||
{
|
||||
@ -56,6 +87,10 @@ struct VectorSpaceOps
|
||||
}
|
||||
}
|
||||
|
||||
//- Apply the inplace binary reduction operation.
|
||||
// \param [in,out] s scalar or component data (non-indexed)
|
||||
// \param [in] vs input vector-space (indexed) data
|
||||
// \param eo binary combine/assign operation
|
||||
template<class S, class V, class EqOp>
|
||||
static inline void SeqOp(S& s, const V& vs, EqOp eo)
|
||||
{
|
||||
@ -66,6 +101,10 @@ struct VectorSpaceOps
|
||||
}
|
||||
}
|
||||
|
||||
//- Apply the inplace binary assignment operation to the components.
|
||||
// \param [in,out] vs1 vector-space (indexed) data
|
||||
// \param [in] vs2 second vector-space (indexed) data
|
||||
// \param eo binary combine/assign operation
|
||||
template<class V1, class V2, class EqOp>
|
||||
static inline void eqOp(V1& vs1, const V2& vs2, EqOp eo)
|
||||
{
|
||||
@ -76,34 +115,51 @@ struct VectorSpaceOps
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class V, class V1, class S, class Op>
|
||||
static inline void opVS(V& vs, const V1& vs1, const S& s, Op o)
|
||||
//- Apply the binary operation between vector-space and scalar data
|
||||
//- and assign the result.
|
||||
// \param [out] vs vector-space (indexed) data
|
||||
// \param [in] vs1 vector-space (indexed) data operand
|
||||
// \param [in] s scalar operand
|
||||
// \param bop binary operation
|
||||
template<class V, class V1, class S, class BinaryOp>
|
||||
static inline void opVS(V& vs, const V1& vs1, const S& s, BinaryOp bop)
|
||||
{
|
||||
// if constexpr (I < N)
|
||||
{
|
||||
vs.v_[I] = o(vs1.v_[I], s);
|
||||
VectorSpaceOps<N, I+1>::opVS(vs, vs1, s, o);
|
||||
vs.v_[I] = bop(vs1.v_[I], s);
|
||||
VectorSpaceOps<N, I+1>::opVS(vs, vs1, s, bop);
|
||||
}
|
||||
}
|
||||
|
||||
template<class V, class S, class V1, class Op>
|
||||
static inline void opSV(V& vs, const S& s, const V1& vs1, Op o)
|
||||
//- Apply the binary operation between scalar and vector-space data
|
||||
//- and assign the result.
|
||||
// \param [out] vs vector-space (indexed) data
|
||||
// \param [in] s scalar operand
|
||||
// \param [in] vs1 vector-space (indexed) data operand
|
||||
// \param bop binary operation
|
||||
template<class V, class S, class V1, class BinaryOp>
|
||||
static inline void opSV(V& vs, const S& s, const V1& vs1, BinaryOp bop)
|
||||
{
|
||||
// if constexpr (I < N)
|
||||
{
|
||||
vs.v_[I] = o(s, vs1.v_[I]);
|
||||
VectorSpaceOps<N, I+1>::opSV(vs, s, vs1, o);
|
||||
vs.v_[I] = bop(s, vs1.v_[I]);
|
||||
VectorSpaceOps<N, I+1>::opSV(vs, s, vs1, bop);
|
||||
}
|
||||
}
|
||||
|
||||
template<class V, class V1, class Op>
|
||||
static inline void op(V& vs, const V1& vs1, const V1& vs2, Op o)
|
||||
//- Apply the binary operation between two vector-space data
|
||||
//- and assign the result.
|
||||
// \param [out] vs vector-space (indexed) data
|
||||
// \param [in] vs1 vector-space (indexed) data operand
|
||||
// \param [in] vs2 vector-space (indexed) data operand
|
||||
// \param bop binary operation
|
||||
template<class V, class V1, class BinaryOp>
|
||||
static inline void op(V& vs, const V1& vs1, const V1& vs2, BinaryOp bop)
|
||||
{
|
||||
// if constexpr (I < N)
|
||||
{
|
||||
vs.v_[I] = o(vs1.v_[I], vs2.v_[I]);
|
||||
VectorSpaceOps<N, I+1>::op(vs, vs1, vs2, o);
|
||||
vs.v_[I] = bop(vs1.v_[I], vs2.v_[I]);
|
||||
VectorSpaceOps<N, I+1>::op(vs, vs1, vs2, bop);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -115,6 +171,12 @@ struct VectorSpaceOps
|
||||
template<direction N>
|
||||
struct VectorSpaceOps<N, N>
|
||||
{
|
||||
template<class Input, class Output>
|
||||
static inline void copy_n(Input, Output) {}
|
||||
|
||||
template<class Output, class T>
|
||||
static inline void fill_n(Output, const T&) {}
|
||||
|
||||
template<class V, class S, class EqOp>
|
||||
static inline void eqOpS(V&, const S&, EqOp) {}
|
||||
|
||||
@ -124,14 +186,14 @@ struct VectorSpaceOps<N, N>
|
||||
template<class V1, class V2, class EqOp>
|
||||
static inline void eqOp(V1&, const V2&, EqOp) {}
|
||||
|
||||
template<class V, class V1, class S, class Op>
|
||||
static inline void opVS(V& vs, const V1&, const S&, Op) {}
|
||||
template<class V, class V1, class S, class BinaryOp>
|
||||
static inline void opVS(V&, const V1&, const S&, BinaryOp) {}
|
||||
|
||||
template<class V, class S, class V1, class Op>
|
||||
static inline void opSV(V& vs, const S&, const V1&, Op) {}
|
||||
template<class V, class S, class V1, class BinaryOp>
|
||||
static inline void opSV(V&, const S&, const V1&, BinaryOp) {}
|
||||
|
||||
template<class V, class V1, class Op>
|
||||
static inline void op(V& vs, const V1&, const V1&, Op) {}
|
||||
template<class V, class V1, class BinaryOp>
|
||||
static inline void op(V&, const V1&, const V1&, BinaryOp) {}
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user