Reputation: 53
I am using auto, decltype and declval in a simple vector class in order to perform basic vector operations e.g. addition of a scalar and a vector. However, I have trouble making it work when trying to add a scalar of type short and a vector of type short.
// Vector.h
#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;
template<typename T> class Vector;
template<typename T> std::ostream& operator<< ( std::ostream& s, const Vector<T>& other );
template<typename T>
class Vector {
std::vector<T> base;
// vector + scalar
template<typename T1, typename T2>
friend auto operator+(const Vector<T1> & lhs, const T2 & scalar) -> Vector<decltype(std::declval<T1>() + std::declval<T2>())>;
friend std::ostream& operator<< <T> ( std::ostream& s, const Vector<T>& other );
public:
Vector();
Vector( const Vector<T>& other );
Vector<T>& operator= ( const Vector<T> &other );
auto& operator[] ( int i );
void insert( const T element );
};
template<typename T>
Vector<T>::Vector() {
}
template<typename T>
Vector<T>::Vector( const Vector<T>& other ) {
base = other.base;
}
template<typename T>
Vector<T>& Vector<T>::operator= ( const Vector<T> &other ) {
base = other.base;
}
template<typename T>
auto& Vector<T>::operator[] ( int i ) {
assert( i >= 0 && i < base.size() );
return base[i];
}
template<typename T>
void Vector<T>::insert( const T element ) {
base.push_back( element );
}
// vector + scalar
template<typename T1, typename T2>
auto operator+(const Vector<T1> & lhs, const T2 & scalar)
-> Vector<decltype(std::declval<T1>() + std::declval<T2>())>
{
typedef decltype(std::declval<T1>() + std::declval<T2>()) T3;
Vector<T3> result;
result.base.reserve(lhs.base.size());
std::transform(lhs.base.begin(), lhs.base.end(), std::back_inserter(result.base),
[&scalar](const T1 & element) { return element + scalar; });
return result;
}
Test program:
// vector_test.cpp
void test_vector_int_scalar_int_addition() {
Vector<int> v;
v.insert( 1 );
v.insert( 2 );
v.insert( 3 );
int s = 2;
Vector<int> res = v + s;
}
void test_vector_short_scalar_short_addition() {
Vector<short> v;
v.insert( 1 );
v.insert( 2 );
v.insert( 3 );
short s = 2;
Vector<short> res = v + s;
}
int main() {
test_vector_int_scalar_int_addition(); // Compiles and works
test_vector_short_scalar_short_addition(); // Does NOT compile
}
Compiling the above fails for the case using short "test_vector_short_scalar_short_addition()", but works fine when using int "test_vector_int_scalar_int_addition()". I get the following error:
error: no viable conversion from 'Vector<decltype(std::declval<short>() + std::declval<short>())>' to 'Vector<short>'
What puzzles me is that is works fine for other types e.g. double, float, long etc. but appears to fail only when using short.
Compiler info:
clang++ --version
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Upvotes: 2
Views: 645
Reputation: 9317
The operands of the built-in addition operator undergo the usual arithmetic conversions, which include integral promotions, which means that most integral types that have an integer conversion rank less than that of int
are converted to int
.
The above means that your Vector<decltype(std::declval<short>() + std::declval<short>())>
is actually Vector<int>
, and the compiler complains that it can't convert it to Vector<short>
.
This can be verified using the following code:
#include <iostream>
#include <type_traits>
int main()
{
std::cout << std::is_same<decltype(std::declval<short>() + std::declval<short>()), int>::value << '\n';
}
which prints 1
.
As an alternative for determining the type of the result, you could consider std::common_type. This will give you a short
if both operands are short
, but will still apply the usual arithmetic conversions if the types are different - for example, unsigned short
and short
will typically yield int
(if sizeof(short) < sizeof(int)
).
Upvotes: 5
Reputation: 96810
The above behavior can be reproduced by this simple example:
short a = 1, b = 1;
auto res = a + b; // decltype(res) == int
As part of the usual arithmetic conversions, both operands (short
s) undergo integral promotion and are converted to int
. This is not the case with float
or double
as they are floating-point types. Since the rank of short
is less than that of int
, the result of the operation is converted to int
, if it can be.
http://en.cppreference.com/w/c/language/conversion#Usual_arithmetic_conversions
Upvotes: 1