I have a template function with a single parameter <T>
, and I would like to make specializations of this function for different integral types. This seemed obvious at first, however after few attempts I found that I do not really understand how the specialization really works here, and how to achieve some degree of portability...
Here's the test program:
// clang test.cc -std=c++11 -lc++
#include <iostream>
#include <typeinfo>
template <typename T> void foo() { std::cout << " foo<T> with T=" << typeid(T).name() << '\n'; }
template <> void foo<int>() { std::cout << " foo<int>\n"; }
template <> void foo<long>() { std::cout << " foo<long>\n"; }
template <> void foo<long long>() { std::cout << " foo<long long>\n"; }
template <> void foo<size_t>() { std::cout << " foo<size_t>\n"; }
// template <> void foo<int64_t>() { std::cout << " foo<int64_t>\n"; } // error
int main () {
std::cout << "sizeof(int)=" << sizeof(int) << ", ";
std::cout << "sizeof(long)=" << sizeof(long) << ", ";
std::cout << "sizeof(long long)=" << sizeof(long long) << ", ";
std::cout << "sizeof(size_t)=" << sizeof(size_t) << "\n";
foo<int>();
foo<long>();
foo<long long>();
foo<size_t>();
foo<ssize_t>();
foo<int8_t>();
foo<int16_t>();
foo<int32_t>();
foo<int64_t>();
foo<uint32_t>();
foo<uint64_t>();
return 0;
}
and on my machine it produces
sizeof(int)=4, sizeof(long)=8, sizeof(long long)=8, sizeof(size_t)=8
foo<int>
foo<long>
foo<long long>
foo<size_t>
foo<long>
foo<T> with T=a
foo<T> with T=s
foo<int>
foo<long long>
foo<T> with T=j
foo<T> with T=y
So here's what I don't understand:
long
and long long
is the same type, why does the compiler allow both specializations to coexist?int64_t
produces an error?foo<int64_t>
resolves as foo<long long>
and not foo<long>
?foo<ssize_t>
resolves as foo<long>
and not foo<long long>
?foo<uint64_t>
does not use specialization foo<size_t>
? 1) If
long
andlong long
is the same type, why does the compiler allow both specializations to coexist?
Because long
and long long
can be implemented over the same low level type but, from the point of view of the language, are different fundamental types.
2) Why adding a specialization for
int64_t
produces an error?
Because std::int64_t
isn't an arithmetic fundamental type but is an alias (defined through typedef
or using
) of another type
3) Why
foo<int64_t>
resolves asfoo<long long>
and notfoo<long>
?
Because, in your platform, std::int64_t
is defined as an alias for long long
, not for long
(or an alias for an alias...); so, in your platform, std::int64_t
is long long
; in different platform, you can have different results
4) Why
foo<ssize_t>
resolves asfoo<long>
and notfoo<long long>
?
Same as std::int64_t
: the type ssize_t
(not a standard type) is an alias (in your platform) for long
, not for long long
5) Why
foo<uint64_t>
does not use specializationfoo<size_t>
?
Because std::uint64_t
and std::size_t
aren't fundamental arithmetic types but both are alias for other types (unsigned long
and unsigned long long
, I suppose) and, in your platform, they are alias of different types
6) Is the behavior that I observe here universal, or machine-specific? How can I be sure that this code is portable?
With exception for point (1) (that is ever true because the difference between long
and long long
is part of the language), is heavily platform dependent.
But it's possible to manage it using, by example, std::is_same
and other type traits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With