The following code compiles and runs:
#include <cinttypes>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <sstream>
#include <stdexcept>
class UnsignedBox {
public:
typedef std::uint64_t box_type;
template<typename UNSIGNED_TYPE,
typename std::enable_if<
std::numeric_limits<UNSIGNED_TYPE>::is_signed==false &&
(sizeof(UNSIGNED_TYPE) >= sizeof(UnsignedBox::box_type)), int>::type = 0
>
UNSIGNED_TYPE toUnsigned()const {
//We've established we're not returning a smaller type so we can just
//return our value.
return value;
}
template<typename UNSIGNED_TYPE,
typename std::enable_if<std::numeric_limits<UNSIGNED_TYPE>::is_signed==false &&
(sizeof(UNSIGNED_TYPE) < sizeof(UnsignedBox::box_type)), int>::type = 0
>
UNSIGNED_TYPE toUnsigned()const {
//We are returning a smaller type so we need a range check.
if(value>static_cast<box_type>(std::numeric_limits<UNSIGNED_TYPE>::max())){
std::ostringstream msg;
msg<<value<<'>'<<
static_cast<box_type>(std::numeric_limits<UNSIGNED_TYPE>::max());
throw std::logic_error(msg.str());
}
return value;
}
UnsignedBox(const box_type ivalue): value(ivalue){}
private:
box_type value;
};
int main(int argc, char*argv[]) {
UnsignedBox box(
static_cast<UnsignedBox::box_type>(
std::numeric_limits<std::uint32_t>::max())+10
);
std::uint64_t v(box.toUnsigned<std::uint64_t>());
std::cout<<v<<std::endl;
try {
std::uint32_t v(box.toUnsigned<std::uint32_t>());
}catch(const std::logic_error err){
std::cout<<err.what()<<std::endl;
}
return EXIT_SUCCESS;
}
Expected output (all supporting platforms):
4294967305
4294967305>4294967295
So far so good.
But what I really want to do (for code clarity):
Is declare something like:
template<typename UNSIGNED_TYPE>
UNSIGNED_TYPE toUnsigned()const;
Then provide specialized implementations such as:
template<typename UNSIGNED_TYPE,
typename std::enable_if<
std::numeric_limits<UNSIGNED_TYPE>::is_signed==false &&
(sizeof(UNSIGNED_TYPE) >= sizeof(UnsignedBox::box_type)), int>::type = 0
>
UNSIGNED_TYPE UnsignedBox::toUnsigned()const {
//We've established we're not returning a smaller type so we can just
//return our value.
return value;
}
template<typename UNSIGNED_TYPE,
typename std::enable_if<std::numeric_limits<UNSIGNED_TYPE>::is_signed==false &&
(sizeof(UNSIGNED_TYPE) < sizeof(UnsignedBox::box_type)), int>::type = 0
>
UNSIGNED_TYPE UnsignedBox::toUnsigned()const {
//We are returning a smaller type so we need a range check.
if(value>static_cast<box_type>(std::numeric_limits<UNSIGNED_TYPE>::max())){
std::ostringstream msg;
msg<<value<<'>'<<
static_cast<box_type>(std::numeric_limits<UNSIGNED_TYPE>::max());
throw std::logic_error(msg.str());
}
return value;
}
But I get this error:
xxx.cpp:nn:20: error: prototype for 'UNSIGNED_TYPE UnsignedBox::toUnsigned() const' does not match any in class 'UnsignedBox'
UNSIGNED_TYPE UnsignedBox::toUnsigned()const {
^ xxx.cpp:nn:23: error: candidate is: template<class UNSIGNED_TYPE> UNSIGNED_TYPE UnsignedBox::toUnsigned() const
UNSIGNED_TYPE toUnsigned()const;
^
Which is odd because if you ask me the prototype of
UNSIGNED_TYPE UnsignedBox::toUnsigned() const
is a great match for
UNSIGNED_TYPE toUnsigned()const;
What am I doing wrong?
PS: This isn't the actual problem but my problem is an analogous where I want to special some templates based on the attributes of primitive types checked at compiled time.
You can't declare a function with one signature:
and then define it with a different signature:
The first one there takes one template argument, the second one takes two - even though one is defaulted. The two have to match completely. So you will need two declarations:
And then the two definitions. Also this itself doesn't work because we're effectively redefining the default template argument, so you'd need to SFINAE on the return type, e.g.:
Although it might be simpler to have the one
toUnsigned()
that forwards to two other member functions based onsizeof
: