Template Specialization

Started by
13 comments, last by Khatharr 8 years, 2 months ago

I has a function:


template <class T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
T function(const std::string& requestString) {
  //things happen
}

Clearly it's only supposed to accept numeric types, but now I want to add a specialization so that it can also handle std::string using different internal logic.

I haven't messed with specialization before, but what I tried based on my reading was this:


template<> std::string function<std::string>(const std::string& requestString) {
  //tomfoolery
}

The compiler doesn't seem to like that. It says that the template can't match the type. I'm thinking this means that it's upset because std::string isn't arithmetic, as is specified by the original. I figured I should declare an "empty" template (just <class T> and no function body contents) and change the arithmetic one to a specialization, but then (predictably) it complained that <int> was ambiguous between the empty one and the arithmetic one.

Anyone know how I can do this?

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
Advertisement

template<typename T>
class acceptsType
{
    template<typename T2, bool isAritmetic = false>
    class helper
    {
    public:

        static const bool value = false;
    };

    template<typename T2>
    class helper<T2, true> // every aritmetic value is accepted
    {
    public:

        static const bool value = true;
    };

    template<>
    class helper<std::string, false> // otherwise, only std::string is accepted
    {
    public:
        static const bool value = true;
    };

public:

    static const bool value = typename helper<T, std::is_arithmetic<T>::value>::value;
};

The idea I had involved generating a helper-template class, that acts as a replacement of "is_aritmetic" when you declare your template function:


template<class T, typename = typename std::enable_if<acceptsType<T>::value, T>::type> // exchange here
T function(const std::string& requestString)
{
    //things happen
}

template<>
std::string function<std::string>(const std::string& requestString) // compiles now
{
    //tomfoolery
}

template<>
int function<int>(const std::string& requestString)
{
}

This compiles for me. I don't know if its the smoothest solution, but at least it gives you explicit control over ie. if you want to add more types without it getting too complicated.

EDIT:

Also, unless you are stuck on pre-C++11, why even go to the trouble of handling this using std::enable_if? If I get it correctly you want to have an compile error if an user tries to use a type thats not aritmetic(or std::string). Why not use an static_assert instead?


template<class T>
T function(const std::string& requestString)
{
    static_assert(std::is_arithmetic<T>::value, "Function only accepts aritmetic values (int, float, ...). Alternatively, try the std::string overload.");
}

Allows you to have an error message that is more clear, and makes specializing way easier.

Also, unless you are stuck on pre-C++11, why even go to the trouble of handling this using std::enable_if? If I get it correctly you want to have an compile error if an user tries to use a type thats not aritmetic(or std::string). Why not use an static_assert instead?


std::enable_if is already C++11. You might be thinking of the old boost::enable_if. That said, I'm not sure about the exact semantics of static_assert but can it really be used to trigger SFINAE, because that's usually the whole point of enable_if.

Khatharr: You might want to take a look at the notes in the cppreference.com documentation. It's not the exact case but might be part of the problem.
I would also try to use enable_if like this


template <class T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type function(const std::string& requestString) // ...
instead. The only times I have used it, I used it like that.


std::enable_if is already C++11. You might be thinking of the old boost::enable_if. That said, I'm not sure about the exact semantics of static_assert but can it really be used to trigger SFINAE, because that's usually the whole point of enable_if.

Ah right, wasn't sure if enable_if is already in C++11 or not.

No, static_assert won't trigger SFINAE, but that was kind of my point - as with the code example given, I don't see much points in him using SFINEA. What he shows and describes is a function that should fail to instantiate if a incompatible type is used - which is what static_assert does, but only with a better error output. I posted both my solution and the comment towards static_assert based on whether he really wants to use SFINEA here, or not :)

I would use SFINAE like this:
template<typename T> void Func(T x, typename std::enable_if<std::is_arithmetic<T>::value, bool>::type = true) {
	cout << "interger type = " << typeid(T).name() << endl;
	}

void Func(std::string) {
	cout << "string type" << endl;
	}
And beaten to it by BitMaster. You can use enable_if as either a parameter passed (often using a default value that is never used) or as a return value. Whichever syntax you prefer (both are kinda messy IMHO).

No, static_assert won't trigger SFINAE, but that was kind of my point - as with the code example given, I don't see much points in him using SFINEA. What he shows and describes is a function that should fail to instantiate if a incompatible type is used - which is what static_assert does, but only with a better error output. I posted both my solution and the comment towards static_assert based on whether he really wants to use SFINEA here, or not smile.png


Yes, but the point of template specialization is in general that you don't want to just restrict it to specific types, you want radically different behavior for some types which you cannot do unless you provide a completely different implementation for those types.
Edit: Actually I think I see what you want to do now. Specialize for std::string as originally planned but insert the static_assert into the unspecialized template part. Yes, that would work. Although if I had the time I would try to investigate why the current scheme does not work. You don't always have such a workaround and there might be some easy skill points to get.

Ryan_001: that won't work in this scenario since the template parameter is only used for the return value and you have to call it function<myType>(...). If the template parameter was used as a function parameter, it could be done like that.
You're right, I missed that.


Yes, but the point of template specialization is in general that you don't want to just restrict it to specific types, you want radically different behavior for some types which you cannot do unless you provide a completely different implementation for those types.

Sure, I agree, might even been that I misunderstood the original question a bit. I thought he wanted to support only is_aritmetic for the base function, and have only a limitied array of other specialisations (of previously known types) like std::string.

In the other case, that he only wants the base function not being callable other than with is_aritmetic compatible types, while still being able to add an arbitrary number of specialisations (possibly by other users), I'd make an even stronger argument for static_assert and against SFINEA.

I'm feeling even more confused about the use of std::enable_if now in the first place, so unless I'm right and its misused here, somebody please explain to me what it does in this particaluar code example.

In the existing function it prevents compilation if the type is not arithmetic. The function body handles arithmetic types, but I also want to start supporting specific non-arithmetic types using the same signature but different function bodies. (Beginning with std::string, yes.)

This is in VS, so the way I have it now does the red underline at the call site when a non-supported type is entered, which is sort of what I'm after.

How would you go about using static_assert for this?

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
I don't think one can decide whether it's misused or not without more context. The thing I would really try to fix it is my suggestion from #3. I have never seen any enable_if used as a defaulted template parameter before and that note from cppreference makes me doubly cautious about that...

This topic is closed to new replies.

Advertisement