vsemenyakin
vsemenyakin

Reputation: 63

Too many destructors called on template classes (N)RVO optimization

I'm trying to write own Smart Pointers (C++11) and stacks with one problem, that can be explained by next example:

#include <iostream>

template<typename T_Type>
class TestTemplateClass {
private:
    T_Type _state;

public:
    TestTemplateClass() : _state() {
        std::cout << "Default constructor" << std::endl;
    }

    TestTemplateClass(int inState) : _state(inState) {
        std::cout << "State constructor" << std::endl;
    }

    template<typename T_OtherType>
    TestTemplateClass(const TestTemplateClass<T_OtherType> &inValue) {
        std::cout << "Template-copy constructor" << std::endl;
    }

    template<typename T_OtherType>
    void operator = (const TestTemplateClass<T_OtherType> &inValue) {
        std::cout << "Operator" << std::endl;
    }

    ~TestTemplateClass() {
        std::cout << "Destructor" << std::endl;
    }
};

TestTemplateClass<int> createFunction() {
    return TestTemplateClass<int>();
}

int main() {
    TestTemplateClass<int> theReference = createFunction();
    std::cout << "Finished" << std::endl;
    return 0;
}

output:

Default constructor
Destructor
Destructor
Finished
Destructor

As you can see, there are to many destructors here. In my mind, it's some problem with interaction between copy elision and template-constructor, but I don't know what may be the reason of such bug. I tried to fix the problem by adding explicit copy-constructor and force compiler use my template-constructor:

// After TestTemplateClass(int inState), but it's not important
explicit TestTemplateClass(const OwnType &inValue) {
    std::cout << "Copy constructor" << std::endl;
}

got next output:

Default constructor
Template-copy constructor
Destructor
Template-copy constructor
Destructor
Finished
Destructor

Here all looks good, but it doesn't look like a clean solution. Are there better alternatives?

Upvotes: 3

Views: 278

Answers (2)

(N)RVO can never introduce a discrepancy between the number of constructor and destructor calls. It's designed to make that principally impossible.

The problem is with your code. According to the rules of the language, a constructor template is never used to produce a copy constructor. The copy constructor is never a template, period.

So your class template does not actually declare a copy constructor, hence the compiler generates the default one (which of course doesn't print anything). If you need any special processing in the copy constructor, you must always declare it manually. A template will never be used to instantiate one.

Upvotes: 5

user1084944
user1084944

Reputation:

Your experiment suggests there isn't a bug at all: the first version simply used the copy constructor which doesn't print anything, and the second version uses a different constructor instead because you effectively disabled it.

(it also looks like whatever compiler and options you're using doesn't do RVO)

Upvotes: 2

Related Questions