Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

std::set of X requires a default constructor for X

24 views
Skip to first unread message

Joseph Hesse

unread,
Feb 2, 2017, 3:37:34 PM2/2/17
to
/*
I have a class X, instances of which I want to put into a std::set of X.
One way to do this is to make instances of X function objects
returning bool.

The code below won't work without the default constructor for X.

Why do I have to provide a default constructor? As the objects are
being inserted into the set they can be compared and put in the tree
structure implementing the set.

Thank you,
Joe
*/

#include <set>

class X
{
private:
int a; // only used to make xobj's function objects
public:
X() : a(1) {} // WON'T WORK WITHOUT THIS
X(int b) : a(b) {}

// following makes instances of X function objects
bool operator () (const X & x1, const X & x2) const
{ return x1.a < x2.a; }
};

int main()
{
std::set<X, X> s; // 2nd X because instances of X are function objects

X x1(3), x2(4), x3(5);

s.insert(x1);
s.insert(x2);
s.insert(x3);

return 0;
}

Bonita Montero

unread,
Feb 2, 2017, 3:50:08 PM2/2/17
to
If you don't want to have a default-constructor use set::emplace().

#include <set>
#include <string>

using namespace std;

struct S
{
S( string s ) { m_s = s; }
string m_s;
};

struct S_less
{
bool operator ()( S sA, S sB )
{
return sA.m_s < sB.m_s;
}
};

int main()
{
set<S, S_less> ss;

ss.emplace( string( "abc" ) );
}

--
http://facebook.com/bonita.montero/

Joseph Hesse

unread,
Feb 2, 2017, 4:09:51 PM2/2/17
to
On 02/02/2017 02:50 PM, Bonita Montero wrote:
> If you don't want to have a default-constructor use set::emplace().
>
> #include <set>
> #include <string>
>
> using namespace std;
>
> struct S
> {
> S( string s ) { m_s = s; }
> string m_s;
> };
>
> struct S_less
> {
> bool operator ()( S sA, S sB )
> {
> return sA.m_s < sB.m_s;
> }
> };
>
> int main()
> {
> set<S, S_less> ss;
>
> ss.emplace( string( "abc" ) );
> }
>
My code doesn't work if I remove the default constructor for X and
change 'insert' to 'emplace'


Joseph Hesse

unread,
Feb 2, 2017, 4:11:18 PM2/2/17
to
On 02/02/2017 02:50 PM, Bonita Montero wrote:
> If you don't want to have a default-constructor use set::emplace().
>
> #include <set>
> #include <string>
>
> using namespace std;
>
> struct S
> {
> S( string s ) { m_s = s; }
> string m_s;
> };
>
> struct S_less
> {
> bool operator ()( S sA, S sB )
> {
> return sA.m_s < sB.m_s;
> }
> };
>
> int main()
> {
> set<S, S_less> ss;
>
> ss.emplace( string( "abc" ) );
> }
>
My code doesn't compile if I remove the default constructor for X and
replace 'insert' with 'emplace'.
Thanks,
Joe

Bonita Montero

unread,
Feb 2, 2017, 4:14:43 PM2/2/17
to
Am 02.02.2017 um 22:09 schrieb Joseph Hesse:

> My code doesn't work if I remove the default constructor for X and
> change 'insert' to 'emplace'

Then remove the default-constructor ....

> X x1(3), x2(4), x3(5);
>
> s.insert(x1);
> s.insert(x2);
> s.insert(x3);

... and replace this with:

s.emplace( 3 );
s.emplace( 4 );
s.emplace( 5 );

--
http://facebook.com/bonita.montero/

Bonita Montero

unread,
Feb 2, 2017, 4:19:13 PM2/2/17
to
Am 02.02.2017 um 22:16 schrieb Stefan Ram:

> The implementation wants to have a constructor.

... if you want to use set::insert(), but you can also use emplace().

--
http://facebook.com/bonita.montero/

Adam C. Emerson

unread,
Feb 2, 2017, 8:11:12 PM2/2/17
to
On 2017-02-02, Joseph Hesse <jo...@gmail.com> wrote:
> I have a class X, instances of which I want to put into a std::set
> of X. One way to do this is to make instances of X function
> objects returning bool.
>
> The code below won't work without the default constructor for X.
>
> Why do I have to provide a default constructor? As the objects are
> being inserted into the set they can be compared and put in the tree
> structure implementing the set.
[snip]

Sir,

Look at the first constructor here:

http://en.cppreference.com/w/cpp/container/set/set

And the answer to your question should be made clear.

Alf P. Steinbach

unread,
Feb 2, 2017, 10:28:17 PM2/2/17
to
On 02.02.2017 21:37, Joseph Hesse wrote:
> #include <set>
>
> class X
> {
> private:
> int a; // only used to make xobj's function objects
> public:
> X() : a(1) {} // WON'T WORK WITHOUT THIS
> X(int b) : a(b) {}
>
> // following makes instances of X function objects
> bool operator () (const X & x1, const X & x2) const
> { return x1.a < x2.a; }
> };
>
> int main()
> {
> std::set<X, X> s; // 2nd X because instances of X are function objects
>
> X x1(3), x2(4), x3(5);
>
> s.insert(x1);
> s.insert(x2);
> s.insert(x3);
>
> return 0;
> }

Well, the main /technical/ problem is that the std::set default
constructor, used above, in order to provide a default comparator
object, requires that the comparator type is default-constructible.

But you don't need to use an instance of your class X directly as
comparator.

You can just define an ordering of X instances by defining an
`operator<`, and then use the default comparator of `std::set`:


[code]
#include <set>

class X
{
private:
int a; // only used to make xobj's function objects
public:
//X() : a(1) {} // WORKS FINE WITHOUT THIS
X(int b) : a(b) {}

// following makes instances of X function objects
auto operator()( X const& x1, X const& x2 ) const
-> bool
{ return x1.a < x2.a; }

// This defines an ordering of X instances, that's all that
// std::set needs. The awkwardness reflects a design issue.
friend
auto operator<( X const& a, X const& b )
-> bool
{ return a.operator()( a, b ); }
};

int main()
{
X x1{ 3 };
X x2{ 4 };
X x3{ 5 };

std::set<X> s; // No 2nd X because "<" is defined for X objects.
s.insert( x1) ;
s.insert( x2 );
s.insert( x3 );
}
[/code]


• • •

A good general guideline is to separate concerns as much as practically
possible.

The X class was and is, apparently, responsible for too much, too many
concerns. I have no idea what the `a` value is all about, but a function
object class that is only about comparing instances of itself seems not
very useful, so presumably the `a` is also about something else. And the
comment on the operator definition, “makes instances”, indicates some
third purpose, that just didn't make it all the way out to the trimmed
example that you posted.

So, generally, separate concerns.

There's even a Wikipedia page about it: <url:
https://en.wikipedia.org/wiki/Separation_of_concerns>.

The `operator<` defined above is an example. It does one single job. And
it technically moves that job out of the class (except for convenience
the definition could have been placed outside the class).


Cheers & hth.,

- Alf

Öö Tiib

unread,
Feb 3, 2017, 8:21:21 AM2/3/17
to
On Thursday, 2 February 2017 22:37:34 UTC+2, Joseph Hesse wrote:
> /*
> I have a class X, instances of which I want to put into a std::set of X.
> One way to do this is to make instances of X function objects
> returning bool.
>
> The code below won't work without the default constructor for X.
>
> Why do I have to provide a default constructor? As the objects are
> being inserted into the set they can be compared and put in the tree
> structure implementing the set.

You don't need to provide default constructor. I explain below inline
with code.

>
> Thank you,
> Joe

You are welcome.

> */
>
> #include <set>
>
> class X
> {
> private:
> int a; // only used to make xobj's function objects
> public:
> X() : a(1) {} // WON'T WORK WITHOUT THIS

Replace above with:
// X() : a(1) {} // WILL WORK WITHOUT THIS


> X(int b) : a(b) {}
>
> // following makes instances of X function objects
> bool operator () (const X & x1, const X & x2) const
> { return x1.a < x2.a; }
> };
>
> int main()
> {
> std::set<X, X> s; // 2nd X because instances of X are function objects

Replace above with:

// non-default constructible comparator needs instance supplied
std::set<X, X> s(0);


>
> X x1(3), x2(4), x3(5);
>
> s.insert(x1);
> s.insert(x2);
> s.insert(x3);
>
> return 0;
> }

Should compile and run.
0 new messages