How to use make_unique with custom deleter lambda for arrays in c++14

Hi ppl,

I am wondering it is possible to use make_shared with custom lambda deleter for arrays.
I am trying to implement my string class using unique_ptr and need to specify custom deleter using lambda.

srting.h
1
2
3
4
5
6
7
  class string final {
    private:
      std::unique_ptr<char[], std::function<void(char [])>> ptr_;
      size_t sz_;
    public:
      string(const string&);
    };


string.cpp
1
2
3
4
5
6
7
8
  string::string(const string& other) {
    sz_ = other.sz_;
    ptr_ = std::make_unique<char [], [] (char ptr[]) { //does not compile
                                     delete ptr;
                                     std::cout << " deleted \n";
                                     }>(sz_ + 1);
    strcpy(ptr_.get(), other.ptr_.get());
  }



error: lambda-expression in template-argument
     ptr_ = std::make_unique<char [], [] (char ptr[]) {
                                      ^
my_string.cpp:12:48: error: no matching function for call to ‘make_unique(size_t)’
                                      }>(sz_ + 1);
                                                ^


Thanks for any help :)
Last edited on
> I am trying to implement my string class using unique_ptr and need to specify custom deleter using lambda.

Why do you need to specify a custom deleter?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <memory>
#include <cstring>

class string final {

    private:

        static constexpr auto my_custom_deleter = []( char p[] ) { // closure object

            std::cout << "executing my_custom_deleter\n" ;
            
            delete[] p ; // replace delete[] p with actual custom deleter code
        };

        std::size_t sz ;
        std::unique_ptr< char[], decltype(my_custom_deleter) > ptr ;

    public:

        // construct string of size n filled with char c
        string( const char* cstr ) : sz( cstr ? std::strlen(cstr) : 0 ), ptr( new char[sz+1], my_custom_deleter ) {
                                                        // replace new char[sz+1] with actual custom allocation code

            if(cstr) std::strcpy( ptr.get(), cstr ) ;
            else ptr[0] = 0 ;
        }

        const char* c_str() const { return ptr.get() ; }
};

int main() {

    string str = "hello world!" ;
    std::cout << str.c_str() << '\n' ;
}

http://coliru.stacked-crooked.com/a/5efb791f0ed9ec39
@JLBorges : Thanks for taking out time to look into this. Please have a look at some specific requirements below.

JLBorges wrote:
Why do you need to specify a custom deleter?

Actually I asked this question for a more generic scenario, this is just one specific example.

[a]
Assume there is a requirement to implement a string class with following constraints :
[1] Do not use new operator explicitly.
[2] Raw char* pointer can not be used.
[3] Use unique_ptr and make_unique for constructing the string object.

[b]
I understand that the customer deleter that you have mentioned above works well. But what if we have to use lambda function as custom deleter in make_unique; is this feature not provided by c++ yet ?

Could you please help me with requirements [a] and [b] ?
Last edited on
For requirement [a] (1, 2 and 3) a custom deleter is not required;
the default deleter (specialization for arrays) would suffice.
https://en.cppreference.com/w/cpp/memory/default_delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <memory>
#include <cstring>

class string final {

    private:

        std::size_t sz ;
        std::unique_ptr< char[] > ptr ;

    public:

        // construct string initialised from cstr
        string( const char* cstr ) : sz( cstr ? std::strlen(cstr) : 0 ),
                                     ptr( std::make_unique< char[] >(sz+1) ) {

            if(cstr) std::strcpy( ptr.get(), cstr ) ;
            else ptr[0] = 0 ;
        }

        friend std::ostream& operator<< ( std::ostream& stm, const string& str ) {

            return stm << str.ptr.get() ;
        }
};

int main() {

    string str = "hello world!" ;
    std::cout << str << '\n' ;
}

http://coliru.stacked-crooked.com/a/17beeaf0f990909d


> What if we have to use lambda function as custom deleter in make_unique?

std::make_unique can't be used for this; it creates a unique pointer where the deleter is the default deleter.
@JLBorges : Thanks alot for the answer.

I understand that the default deleter (specialization for arrays) would suffice for requirements [1],[2] & [3] but don't we lose the choice of do something custom with default_delete.


My apologies for not adding this requirement before.

What if we have one more requirement ?

[1] Do not use new operator explicitly.
[2] Raw char* pointer can not be used.
[3] Use unique_ptr and make_unique for constructing the string object.
and
[4] Print a message : "unique_ptr is going out of scope" when the destructor for unique_ptr is called
(Please note the requirement is for uniue_ptr destructor and not string).

You have explained how we can use a custom deleter with unique_ptr here : http://www.cplusplus.com/forum/general/247955/#msg1093306
but it does not use make_unique.

Is it that we can't specify a custom deleter with make_unique at all ?

Thanks again :)
> [4] Print a message : "unique_ptr is going out of scope" when the destructor for unique_ptr is called
> (Please note the requirement is for uniue_ptr destructor and not string).

Write a noisy wrapper for the unique pointer. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <memory>
#include <cstring>

class string final {

    private:

        struct unique_ptr_noisy_wrapper : public std::unique_ptr< char[] > {

            using base = std::unique_ptr< char[] > ;

            unique_ptr_noisy_wrapper( base&& p ) : base( std::move(p) ) {}
            ~unique_ptr_noisy_wrapper() {

                const char* cstr = get() ;
                std::cout << "unique_ptr @" << static_cast<const base*>(this)
                          << " (raw pointer " << static_cast<const void*>( get() )
                          << ", \"" << ( cstr && cstr[0] ? get() : "<empty>" )
                          << "\") is being destroyed\n" ;
            }

            using base::base ;
        };

        std::size_t sz ;
        unique_ptr_noisy_wrapper ptr ;

    public:

        // construct string initialised from cstr
        string( const char* cstr ) : sz( cstr ? std::strlen(cstr) : 0 ),
                                     ptr( std::make_unique< char[] >(sz+1) ) {

            if(cstr) std::strcpy( ptr.get(), cstr ) ;
            else ptr[0] = 0 ;
        }

        friend std::ostream& operator<< ( std::ostream& stm, const string& str ) {

            return stm << str.ptr.get() ;
        }
};

int main() {

    const string str_bye = "*** bye bye! ***" ;
    const string words[] = { "shoe.", "my", "Buckle", "", "two,", "One," };
    const string str_hello = "*** hello world! ***" ;
}

http://coliru.stacked-crooked.com/a/013f370d7c9272ca
Got it @JLBorges :)
Thanks alot.
Topic archived. No new replies allowed.