Question

I'm writing a C++11 STL-compatible allocator, and I was wondering how to detect the types for which it is safe not to call their destructor (in allocator<T>::destroy method.)

I have already written the allocator (a simple one) and as far as I can tell, it does work. The reason I ask is that I'm getting warnings in my code (i.e. in the destroy method of my allocator.) I'm using VS2013 (vc12) at the highest warning level, and the warning is:

warning C4100: 'c' : unreferenced formal parameter

in this method:

template <typename T>
class MyAlloc
{
    ...

    template <typename C>
    void destroy (C * c) // <-- this is the 'c' that the warning is referring to
    {
        c->~C ();
    }

    ...
};

As you can see, both the warning and the code are pretty simple and straightforward. It seems to me that the warning is issued because some of the classes that this allocator gets used for don't have destructors (e.g. because they are POD, etc.) Subsequently, the compiler removes the call to the destructor in the above function when the allocator is being instantiated for such classes and then, seeing that the function body is empty and the argument is unused, issues the warning.

I'm thinking that I can write two versions of the above destroy method, overloading using enable_if, and leaving the body empty and the argument unnamed in the overload that is for classes that don't need destruction. Would this work?

On the other hand, this warning is a very small inconvenience. I can disable this particular warning, and it won't have much impact on my codebase. After all, this is hardly a useful warning.

However, if I do try to change my code and detect classes that don't need destruction, but do it unreliably and poorly, I open the floodgates for all sorts of pain and suffering. Because if I happen to not destruct an instance of a class that does need destruction, only the gods know what can (and would) go wrong! So, if there isn't a 100% reliable and robust method to detect such classes and handle them, I prefer to leave in the warning and even ship with the warning.

To reiterate, my question is in three parts:

  1. Is my analysis of the cause of the warning correct?
  2. How can I determine when it is safe not to call a type's destructor. In other words, when does a type's destructor has absolutely no effect and how can I detect this (using type traits, etc.)?
  3. Is this detection always reliable and completely robust?

And a bonus question:

I tried this overload just to see whether it'd work:

template <typename C>
std::enable_if<std::is_trivially_destructible<C>::value>
destroy (C *)
{
}

template <typename C>
std::enable_if<!std::is_trivially_destructible<C>::value>
destroy (C * c)
{
    c->~C ();
}

Note that I'm not saying that using std::is_trivially_destructible<> is the way to go; I just wanted to try and see whether enable_if works in this context. But now I'm getting many errors like this:

error C2668: 'MyAlloc<Whatever>::destroy' : ambiguous call to overloaded function
could be 'std::enable_if<false,void> MyAlloc<Whatever>::destroy<SomeType>(C *)'
or       'std::enable_if<true,void> MyAlloc<Whatever>::destroy<SomeType>(C *)'

Seems I'm doing something horribly wrong with enable_if. Where am I going wrong? Shouldn't the enable_if<false,...> alternate be dropped from resolution due to SFINAE? Does SFINAE happen at class scope as well? I'll be thankful for any help in this regard as well.

Was it helpful?

Solution 2

Is my analysis of the cause of the warning correct?

I think so. There are several ways to silent an unused variable warning (and generally having specific macro/function to do that is helpful)

How can I determine when it is safe not to call a type's destructor. In other words, when does a type's destructor has absolutely no effect and how can I detect this (using type traits, etc.)?

I would silent the warning. but if I have to use traits, I would use std::is_trivially_destructible.

Is this detection always reliable and completely robust?

Seem not fully as they change the definition for C++14.

About your error:

The correct syntax is (note the typename .. ::type)

template <typename C>
typename std::enable_if<std::is_trivially_destructible<C>::value>::type
destroy (C *){}

With your syntax, you return std::enable_if<std::is_trivially_destructible<C>::value> which exists (so it is not removed by SFINAE and then you have two identical methods with different return types)

std::enable_if<bool condition, typename T>::type only exists when the condition is true (and is equal to the second type defaulted to void).

OTHER TIPS

Microsoft say it's a bug ("limitation") of their compiler.

Inventing complex template-based workarounds is an interesting intellectual challenge, but in a code review for a real product I would throw any such thing away faster than you can say "overengineered".

template <typename C>
void destroy (C * c) // <-- this is the 'c' that the warning is referring to
{
    (void)c; // shut up you stupid compiler
    c->~C ();
}

Regarding 1. Something is missing (code/warnings/error messages).

This works just fine:

template <typename T>
void destroy(T* p) {
    p->~T();
}

int main()
{
    int* i;
    destroy(i);
    return 0;
}

Compiled with g++ -std=c++11 -Wall -pedantic

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top