11

Basically I want my range type to be implicitly convertible from Range<const char> to Range<const unsigned char>. std::enable_if seems impossible because the function takes no arguments and has no return. Whats the work around?

Here is basically what I tried:

template<typename T>
class Range{
    T* begin_;
    T* end_;
public:
    Range(T* begin,T* end):begin_{begin},end_{end}{}
    template<int N>
    Range(T (&a)[N]):begin_{static_cast<T*>(&a[0])},end_{static_cast<T*>(&a[N-1])}{}
    T* Begin(){return begin_;}
    T* End(){return end_;}
    operator typename std::enable_if<std::is_same<T,const char>::value,Range<const unsigned char>&>::Type (){
        return *reinterpret_cast<Range<const unsigned char>*>(this);
    }
};
odinthenerd
  • 5,422
  • 1
  • 32
  • 61
  • why not using `std::is_same<>` instead of `Loki::IsSameType<>` ?? – Walter Aug 07 '13 at 10:11
  • I'm programming bare metal on an ARM cortex and it was easier at first to get Loki working, so good point and I changed it ;) – odinthenerd Aug 07 '13 at 10:24
  • Do you really need a conversion operator? Or could you live with `operator=` and constructor? – Walter Aug 07 '13 at 10:39
  • `std::enable_if` has a member type(def) `type` (small `t`), not `Type`. – dyp Aug 07 '13 at 10:40
  • You could use a [`std::conditional`](http://en.cppreference.com/w/cpp/types/conditional) to switch between `Range` and `void` for the conversion operator (according to clang++, a conversion operator to `void` is legal and never used). Or you could use specialization of the class template. – dyp Aug 07 '13 at 10:44

1 Answers1

18

Make it a template with a dummy parameter that defaults to T - this is to postpone type deduction to the point where the function gets instantiated, otherwise SFINAE doesn't work. Then you do the check you want in default value of another parameter.

template<
    typename U = T,
    typename = typename std::enable_if< std::is_same<U,const char>::value >::type
>
operator Range<const unsigned char>() {
    return *reinterpret_cast<Range<const unsigned char>*>(this);
}

Note: With C++17 or later, the above can be shortened slightly to:

template<typename U = T, typename = std::enable_if_t<std::is_same_v<U,const char>>>
operator Range<const unsigned char>() {
    return *reinterpret_cast<Range<const unsigned char>*>(this);
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684
jrok
  • 54,456
  • 9
  • 109
  • 141
  • @joik thanks for pointing out my error with conflicting conversion operators, I removed it. – odinthenerd Aug 07 '13 at 11:13
  • Also note the somewhat unusual "typename = typename ...". It would not work for me if I used a type name. – Asher Nov 04 '15 at 07:10
  • @einpoklum Your improvement doesn't work an any major compiler: https://godbolt.org/z/MrxzsPq5a original works on gcc fails on clang: https://godbolt.org/z/K7639Ejod (do not see result for MSVC - performance issue). – Marek R Feb 05 '23 at 14:03
  • 1
    @MarekR: That's because I deleted the `typename =`, which I shouldn't have. Fixed. – einpoklum Feb 05 '23 at 16:10
  • Yes I've missed that too. Sadly clang has strange complains: https://godbolt.org/z/fTKEnPcTq `error: conversion function converting 'Range' to itself will never be used` I wonder why SFINAE didn't kick in for `Range`. – Marek R Feb 05 '23 at 21:10
  • Got version which works on all compilers: https://godbolt.org/z/r3z1GhPPf – Marek R Feb 05 '23 at 21:18