0

Consider the code below. My intention is to use a different conversion operator(implicit) depending on the template type argument of struct B.

#include <type_traits>
#include <iostream>

using namespace std;

// just to simplify syntax
template <bool Tp_bool, typename Tp = void>
using Enable_if = typename std::enable_if<Tp_bool, Tp>::type;

template<typename T>
struct B {
    T m_value;

    explicit B(T val = T{}) :
        m_value{val}
    { }

    template<typename Q = T, Enable_if<sizeof(Q) < sizeof(int), int> = 0 >
    operator size_t(){
        cout << "narrow T\n";
        return static_cast<size_t>(m_value);
    }

    template<typename Q = T, Enable_if<sizeof(Q) >= sizeof(int), int> = 0 >
    operator Q(){
        cout << "wide T\n";
        return m_value;
    }
};

int main() {
    B<int> b{5};

    char* pc = new char[b]; // !!!compile-time error here!!!

    if(b == 5) /*no-op*/;   // unrem the line above to see that SFINAE works fine

    return 0;
}

This is the error I get on the line I indicated in the code block above:

error: default type conversion can't deduce template argument for 
'template<class Q, typename std::enable_if<(sizeof (Q) >= sizeof (int)), int>::type <anonymous> >
B<T>::operator Q() [with Q = Q;
    typename std::enable_if<(sizeof (Q) >= sizeof (int)), int>::type <anonymous> = <enumerator>; T = int]'

What is causing this, and how can I make it work?

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
Kemal
  • 849
  • 5
  • 21
  • 4
    What is `Enable_if`? Please show the *actual* code. – Some programmer dude Jan 23 '17 at 09:33
  • @Someprogrammerdude You're right, so sorry. Now done. – Kemal Jan 23 '17 at 09:37
  • Did you mean `operator size_t` or `operator Q` in the narrow case? – Martin Bonner supports Monica Jan 23 '17 at 09:37
  • You might use `std::enable_if_t` instead of your custom alias. – Jarod42 Jan 23 '17 at 09:37
  • @MartinBonner I mean `operator size_t`. – Kemal Jan 23 '17 at 09:39
  • There's also a missing closing `}` for the struct, and missing `#include `. – Kiril Kirov Jan 23 '17 at 09:39
  • @KirilKirov Corrected. This is an embarrassing day. – Kemal Jan 23 '17 at 09:42
  • 1
    The comparison with literal 5 is casting to int, so the narrow case (which returns a size_t) won't be particularly signifcant. In the array case, you are casting to size_t, so possible candidates are `operator size_t()`, *and* `operator size_t`, `operator size_t`, `operator size_t`, etc, etc. Which one of those last ones did you have in mind? – Martin Bonner supports Monica Jan 23 '17 at 09:42
  • I don't think you added the closing bracket on the right place.. also, you need `;` after it. Please review this code and paste the real one, if it's not. – Kiril Kirov Jan 23 '17 at 09:43
  • ... and run the code you are giving us through a compiler (to make sure it gives the same error). – Martin Bonner supports Monica Jan 23 '17 at 09:44
  • I got strange result [Demo](http://coliru.stacked-crooked.com/a/7a6d809f1be85528). Clang gives strange warnings, and gcc gives error at the 2 implicit conversions... – Jarod42 Jan 23 '17 at 09:47
  • @Jarod42 [This](http://cpp.sh/5tn62) is the error I was talking about. – Kemal Jan 23 '17 at 10:01
  • With [minor changes](http://coliru.stacked-crooked.com/a/b068be947533dffa) it works on Clang so it looks like a GCC bug. – Potatoswatter Jan 23 '17 at 10:13
  • @MartinBonner In comparison with literal 5, this was my assumption: there is no comparison operator defined for B so `b` is cast to size_t, then literal 5 is cast to size_t as well through implicit conversion(supposing size_t is unsigned int). In array size case, I am assuming b will be cast to size_t since Q defaults to T which is substituted as `int` for B. Pls check the link 2 posts up where I duplicated the error. – Kemal Jan 23 '17 at 10:18
  • @KirilKirov [Here](http://cpp.sh/5tn62) is the error I was mentioning. – Kemal Jan 23 '17 at 10:21
  • Why do you expect `B` to be cast to `size_t` rather than `int`? – Martin Bonner supports Monica Jan 23 '17 at 10:57
  • Incidentally, I know of no platform where `size_t` is `unsigned int` (it's legal, but I don't know of any). `size_t` is usually `unsigned long`, except on 64-bit Windows where it is `uint64_t`. – Martin Bonner supports Monica Jan 23 '17 at 10:57
  • @MartinBonner Kind correction duely noted. I was expecting `B` to be cast to `size_t`(now `unsigned int` as you pointed out) because it has no other conversion operators. Only to size_t. I don't see how would it be converted to `int`. – Kemal Jan 23 '17 at 12:17
  • The second template conversion operator (the `wide` one) will be enabled for Q == int, so there will be conversion to `int` available. – Martin Bonner supports Monica Jan 23 '17 at 12:29
  • @MartinBonner You are right. For some reason, I was thinking of `B` instantiating the narrow version, which is *obviously* not correct. So I see your point on literal comparison. I am still not clear on array size case, though. Assuming array size(i.e. `b`) is expected to be `size_t` as you suggest, I don't understand why `operator size_t()`, etc. would be considered, as `Q` defaults to `int`. I expect the only operator to consider is to be `operator size_t()`, since, as I said, `Q` defaults to `int`. – Kemal Jan 23 '17 at 13:45

0 Answers0