Reputation: 1366
I am creating a mathematical vector class.
I would like for its operators to provide conversions / warning behaviour between types similar to how conversions work with POD types in C++, e.g.:
//{
// auto f = 1.f;
// auto d = 2.0;
// f *= d; // warns about possible loss of data
// d *= f; // fine
// auto d2 = f * d; // fine (f promotion, d2 is double)
//}
As I understand it, I need to use std::common_type to find the correct type. Unfortunately, I get compiler errors like:
1>C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\type_traits(1446): error C2446: ':' : no conversion from 'Testing::Vector<float,3>' to 'float'
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> TestVector.cpp(152) : see reference to class template instantiation 'std::common_type<float,Testing::Vector<float,3>>' being compiled
Here is the relevant code:
template<class ElementT, unsigned int Dimensions>
class Vector
{
public:
typedef ElementT ElementT;
static const unsigned int Dimensions = Dimensions;
typedef std::array<ElementT, Dimensions> DataT;
Vector():
data() { }
explicit Vector(std::initializer_list<ElementT> values):
data()
{
std::copy(values.begin(), values.end(), data.begin());
}
template<class E>
explicit Vector(Vector<E, Dimensions> const& other):
data()
{
std::copy(other.Data().begin(), other.Data().end(), data.begin());
}
Vector& operator*=(ElementT value)
{
for (auto& e : data)
e *= value;
return *this;
}
Vector& operator*=(Vector const& other)
{
for (auto i = 0u; i != data.size(); ++i)
data[i] *= other.data[i];
return *this;
}
// etc. ...
// Warnings are still propagated from the copy constructor
// if this is used with inappropriate types...
template<class E>
operator Vector<E, Dimensions>() const
{
return Vector<E, Dimensions>(*this);
}
DataT& Data()
{
return data;
}
DataT const& Data() const
{
return data;
}
private:
friend std::ostream& operator<<(std::ostream& stream, Vector v)
{
for (auto const& e : v.data)
stream << e << " ";
return stream;
}
DataT data;
};
template<class E, unsigned int D>
Vector<E, D> operator*(Vector<E, D> const& v, E value)
{
auto result = Vector<E, D>(v);
result *= value;
return result;
}
template<class E, unsigned int D>
Vector<E, D> operator*(Vector<E, D> const& v1, Vector<E, D> const& v2)
{
auto result = Vector<E, D>(v1);
result *= v2;
return result;
}
template<class E, class T, unsigned int D>
Vector<std::common_type_t<E, T>, D> operator*(Vector<E, D> const& v, T value)
{
auto result = Vector<std::common_type_t<E, T>, D>(v);
result *= value;
return result;
}
template<class E1, class E2, unsigned int D>
Vector<std::common_type_t<E1, E2>, D> operator*(Vector<E1, D> const& v1, Vector<E2, D> const& v2)
{
auto result = Vector<std::common_type_t<E1, E2>, D>(v1);
result *= v2;
return result;
}
void TestVector()
{
std::cout << "Testing Vector" << std::endl;
using Vec3 = Vector<float, 3u>;
// Same types. All fine.
{
auto v1 = Vec3({ 1, 2, 3 });
auto v2 = Vec3({ 1, 2, 3 });
v1 *= 2.f;
v1 *= v2;
std::cout << v1 << std::endl;
}
{
auto v1 = Vec3({ 1, 2, 3 });
auto v2 = Vec3({ 1, 2, 3 });
std::cout << (v1 * 2.f) << std::endl;
std::cout << (v1 * v2) << std::endl; // causes problems with std::common_type?
}
{
auto v1 = Vector<float, 3u>({ 1, 2, 3 });
auto v2 = Vector<double, 3u>({ 1, 2, 3 });
v1 *= 2.0; // should probably produce a warning, but doesn't? :(
v1 *= v2; // compiles with warning :)
v2 *= v1; // fine :)
std::cout << v1 << std::endl;
}
{
// The std::common_type versions seem to conflict?
auto v1 = Vector<float, 3u>({ 1, 2, 3 });
auto v2 = v1 * 2.0; // v1 promotion -> should create a Vector<double, 3u>
auto v3 = v1 * v2; // v1 promotion -> should create another Vector<double, 3u>
std::cout << v2 << std::endl;
}
}
So:
Thanks.
Upvotes: 1
Views: 966
Reputation: 392931
If you just use the same operations in the implementations, the instantiation will generate the same warnings.
You can try this example:
#include <type_traits>
template <typename T>
struct Vect3 {
T a,b,c;
template <typename U, typename V = typename std::common_type<T, U>::type>
Vect3<V> operator*(U u) const { return {a*u,b*u,c*u}; }
template <typename U>
Vect3& operator*=(U u) { a*=u; b*=u; c*=u; return *this; }
};
int main()
{
auto f = Vect3<float>{1,2,3};
auto d = 2.0;
auto common = f * d; // fine!
f *= d; // warns about possible loss of data in the instantion of operator*=
}
GCC doesn't warn even with -std=c++11 -Wall -pedantic -Wextra -Wconversion -Wconversion-extra
, but neither does it for your original example, so I'm guessing there is no corresponding warning in GCC
Upvotes: 1