15

Is there any way to make a complete copy of a type so that they can be distinguished in template deduction context? Take the example:

#include <iostream>

template <typename T>
struct test
{
    static int c()
    { 
        static int t = 0;
        return t++;
    }
};

typedef int handle;

int main()
{
    std::cout << test<int>::c() << std::endl;
    std::cout << test<handle>::c() << std::endl;
    return 0;
}

Since typedef only makes an alias for a type, this prints 0, 1 instead of the desired 0, 0. Is there any workaround for this?

mkrieger1
  • 10,793
  • 4
  • 39
  • 47
Veritas
  • 2,060
  • 2
  • 13
  • 35
  • 2
    Take a look at [Boost strong typedef](http://www.boost.org/doc/libs/1_57_0/libs/serialization/doc/strong_typedef.html). – interjay Mar 07 '15 at 16:01

2 Answers2

11

Quoting cplusplus.com,

Note that neither typedef nor using create new distinct data types. They only create synonyms of existing types. That means that the type of myword above, declared with type WORD, can as well be considered of type unsigned int; it does not really matter, since both are actually referring to the same type.

Since int and handle are one and the same, the output 0 1 is expected.

There's a workaround though, as @interjay suggests.

You can use BOOST_STRONG_TYPEDEF.

BOOST_STRONG_TYPEDEF( int , handle );
akim
  • 6,950
  • 1
  • 38
  • 51
shauryachats
  • 8,610
  • 4
  • 32
  • 47
  • 2
    The question is to find an alternative to `typedef` that won't have this behavior. – interjay Mar 07 '15 at 15:58
  • I don't think there is an alternative to `typedef`, that is the point I'm trying to highlight. – shauryachats Mar 07 '15 at 16:01
  • 2
    There isn't one built into the language, but that doesn't mean one can't be built (e.g. the link I posted above). – interjay Mar 07 '15 at 16:02
  • Yes, you are right. If you are not planning to post an answer, may I edit my answer to add your link? – shauryachats Mar 07 '15 at 16:03
  • Thanks to you @interjay, I learnt a new thing today. :) – shauryachats Mar 07 '15 at 16:07
  • 1
    Yes I was aware that typedef doesn't provide the desired behavior. The link looks very promising! – Veritas Mar 07 '15 at 16:13
  • 1
    BOOST_STRONG_TYPEDEF has two problems. It doesn't work for user-defined types and multiple typedefs of the same primitive are not distinguished in template deductions. I wrote an answer addressing these problems. – Veritas Mar 08 '15 at 18:58
  • That's great work. But, you didn't have to unaccept my answer for that. – shauryachats Mar 09 '15 at 01:49
  • 1
    @shauryachats actually I think I missclicked somewhere. Back to accepted! – Veritas Mar 09 '15 at 09:02
  • @Veritas only supporting privitive types is no problem imho, but I found no evidence for your second claim. Afaik the purpose of BOOST_STRONG_TYPEDEF is introduce a type that is also for templates considered as a distinct type. Do you have a reference/example ? – 463035818_is_not_a_number Mar 27 '20 at 08:59
  • @idclev463035818 I made an example program to play with BOOST_STRONG_TYPEDEF which confirms the second claim: https://gist.github.com/MaxBarraclough/088047135c1a1d7767f5ec82199c7815 – Max Barraclough Nov 09 '20 at 22:39
  • @MaxBarraclough the "second claim" is that they are not distinguished in template deductions. Thats not what your example is about. See here for a working example about template parameter deduction: https://godbolt.org/z/fWEjMj. Actually it is similar to what OP is asking for here. And it does work! In your example there are implicit conversions, thats a different issue. I have to do some research to say anything about that – 463035818_is_not_a_number Nov 10 '20 at 08:52
  • @MaxBarraclough BOOST_STRONG_TYPEDEF is just not meant to surpress implicit conversions, its part of their example https://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/strong_typedef.html. See `y = x; // other operations permitted as a is converted as necessary` what you call "RENDERING THE WHOLE THING POINTLESS." is by design. You misunderstood what BOOST_STRONG_TYPEDEFs are made for – 463035818_is_not_a_number Nov 10 '20 at 08:56
  • @MaxBarraclough Suppose `char` is unsigned then you also do not get an error for assigning a `char` to a `unsigned char`, yet they are two distinct types – 463035818_is_not_a_number Nov 10 '20 at 08:57
  • here again the link to a working demo: https://godbolt.org/z/fWEjMj. Maybe you want to modify it and add it to the answer – 463035818_is_not_a_number Nov 10 '20 at 08:59
  • @idclev463035818 *In your example there are implicit conversions, thats a different issue.* Thanks I'd missed that, makes sense. I might add a static assert showing that they're different types. – Max Barraclough Nov 10 '20 at 11:56
  • @MaxBarraclough `typedef int x;` is only introducing a different name for `int`, you cannot have a distinct type without some boilerplate and only thats what the strong typedef gives you. Having a different type does not imply that you cannot convert between them – 463035818_is_not_a_number Nov 10 '20 at 12:04
  • @idclev463035818 Thanks, but I'm familiar with how typedef works. I've already acknowledged the point about implicit conversions. – Max Barraclough Nov 10 '20 at 12:10
5

Either as suggested BOOST_STRONG_TYPEDEF

template<typename>
struct test {
    static int c() {
        static int t = 0;
        return t++ ;
    } 
};

//Instead of 
//typedef int handle

BOOST_STRONG_TYPEDEF(int , handle) ;  

int main() {

    std::cout << test<int>::c() << std::endl
    std::cout << test<handle>::c() << std::endl ;
    return 0;
}

Output : 0 0 , because handle is not int but a type implicitly convertable to int .

if you don't want to use BOOST_STRONG_TYPE then simply add second parameter to your class template :

template<typename, unsigned int N>
struct test {
    static int c() {
        static int t = 0;
        return t++ ;
    }

};

Thus making test<int, 0> and test<handle, 1> different types

int main() {

    std::cout << test<int, 0>::c() << std::endl ;
    std::cout << test<handle,1>::c() << std::endl ;
    return 0;
} 

Output : 0 0

You can also add macro to generate your types :

#define DEFINE_TEST_TYPE(type) \
typedef test<type, __COUNTER__> test_##type;


template<typename, unsigned int N>
struct test {    
     static int c() {
        static int t = 0;
        return t++ ;   
    }
};

typedef int handle ;

DEFINE_TEST_TYPE(int) ;
DEFINE_TEST_TYPE(handle) ;

int main() {
    std::cout << test_int::c() << std::endl ;
    std::cout << test_handle::c() << std::endl ;
    return 0;
}
  • Regarding your now-deleted question abour your open-source project, you could ask that on relevant Reddit communities. If you do ask it in more than one sub though, do ensure you declare your cross-posting, so people can avoid making duplicate answers. – halfer Jun 04 '17 at 20:35