Click here to Skip to main content
15,884,473 members
Please Sign up or sign in to vote.
3.50/5 (2 votes)
See more:
Hi all,
I am exploring C++11 templates with Microsoft's VS2013. Recently I came across the following problem:
* I have a template function with a dummy function body.
* I want to create a specialization that works for all suitable types, e.g. all integer types, all floating-point types, or all character types.
Let me give you a simple example. The function Invert should return the bit-inverted parameter value, which of course makes no sense for most data types, so the dummy implementation returns 0:
C++
template<typename T> T Invert(T data)
{ // dummy
    return T();
}

Now I can start to specialize the template:
C++
template<> int Invert(int data)
{ // specialized
    return ~data;
}
template<> unsigned Invert(unsigned data)
{ // specialized
    return ~data;
}
//etc.

for all suitable data types: char, int, short, long, long long, in their signed and unsigned variants. That creates multiplied code. So I thought there must be a better solution, and my guess is that SFINAE is the answer.
I created a template class in type_traits style
C++
template<typename T> struct IsSuitableType
{
    static bool const value = std::is_integral<T>::value && !std::is_enum<T>::value && !std::is_same<T, bool>::value;
};

to make expressions shorter, and then wrote "generic" specialization:
C++
template<typename T> std::enable_if_t<IsSuitableType<T>::value, T> Invert(T data)
{ // specialized
    return ~data;
}

but compiling test code where Invert<int> is called gives me
error C2668: 'Invert' : ambiguous call to overloaded function
could be 'int Invert<int>(T)'
or       'T Invert<int>(T)'

(For illustration, the test code is simply: int i = Invert(3);)
This only works if I modify the original template definition so that I now have
C++
template<typename T> std::enable_if_t<!IsSuitableType<T>::value, T> Invert(T data)
{ // dummy
    return T();
}
template<typename T> std::enable_if_t<IsSuitableType<T>::value, T> Invert(T data)
{ // specialized
    return ~data;
}

which makes the dummy function less generic, and I would have to modify the template every time I add some new specialization, which is not how (in my understanding) template programming should be done.

How can I solve this problem?
Posted

The following links might help :

https://msdn.microsoft.com/en-us/library/ee361639.aspx[^]
http://en.cppreference.com/w/cpp/types/enable_if[^]

By the way, I tried your program and it works perfectly so I'm not sure of the code that you have kept...
C++
#include <type_traits>

template<typename T> struct IsSuitableType
{
    static bool const value = 
        std::is_integral<T>::value && 
        !std::is_enum<T>::value && 
        !std::is_same<T>::value;
};

template<typename T> std::enable_if_t<IsSuitableType><T>::value, T> Invert(T data)
{ // specialized
	return ~data;
}

And somewhere in a function:
C++
int inv = Invert(3);


And there will be a compilation error if you try with parameters that are not adequate (bool, const char *, ...)
 
Share this answer
 
v2
Comments
Philippe Mori 11-Apr-15 10:00am    
Above code was tested with Visual Studio 2013 (Express for Windows), Update 2.
hans.sch 11-Apr-15 12:35pm    
Thanks Philippe for the quick reply. The issue is that I don't want a compiler error when using an inadequate type but I want the 'dummy' implementation be used. The background is that the call to Invert goes into a template function, and for inadequate types, it simply doesn't get called, so I don't care about the return value. All I need is that it compiles error free.

To see the compiler error in the sample, add the dummy implementation above the specialized one.

I know the 'dummy' code won't compile if T::T() is not public, but I'll deal with this later...
Philippe Mori 11-Apr-15 14:09pm    
It does not make sense that an invert function would do nothing... You always want to detect errors at compile time. If not, then why using C++ and templates?
hans.sch 12-Apr-15 2:38am    
This is not about inverting a bit pattern, rather it is about learning about templates. In the long run, other stuff will take the place of bit inversion. My question is not whether it makes sense (I agree it doesn't) but whether it can be done.

I want to write a template implementation that does 'A' for for all types, except for a certain category of types (categorized by IsSuitableType<t>::value) for which it does 'B' - but 'B' won't compile for non-suitable types. 'A' could be an empty function body (if it were not that the function must return a value).
While playing with other aspects of templates, I came across the solution. It lacks some elegance because it requires a dummy parameter.

C++
template<typename U>
U Invert2(U const &data, ...)
{ // default
    return data;
}

template<typename U>
std::enable_if_t<is_suitable_type<U>::value, U> Invert2(U const &data, U const*)
{ // specialized for data types for which bit inversion makes sense
    return ~data;
}

template<typename T>
inline T Invert(T const &data)
{
    return Invert2(data, &data);
}

template<typename T>
void RunTest()
{
    for (auto itr : CTestData<T>::m_Data)
    {
        T data = itr.m_Data;
        T expect = itr.m_Expect;
        T actual = Invert(data);
        ASSERT(expect == actual);
    }
}



I defined two overloads of the Invert2 function, both of which are template functions. If U is not a suitable type, then only the first overload is available. Otherwise, both overloads are available, and the compiler will pick the one that matches best. Through the second (dummy) parameter, the compiler finds that the second overload is a better match than the first one.

The Invert function is only a wrapper that hides passing the second parameter.

The solution deviates from my original problem insofar as the "default" overload returns its argument, and not U(), because my test included a class that had a private default constructor.

The code in itself is no good for solving everyday coding problems. View it as an example of what one can do with C++11 templates.
 
Share this answer
 
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900