Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Other way to prohibit a certain C++ class construction except than declaring the constructor private?

Say I have a class with some const reference member variable and I would like to forbid a certain type of construction. So I would declare the according constructor private. Of course, a constructor must initialise all const reference member variables of the class. Doing so, however, results in odd looking code:

class A {
};

class B {
  B(const A& a): host(a) {}
private:
  B():host(A()) {}   // This is ugly and not needed !!
  const A& host;
};

Is there another way to prohibit a certain construction type except than declaring the constructor private? I do not want to let the compiler write a constructor for me.

like image 294
ritter Avatar asked Sep 06 '11 09:09

ritter


2 Answers

Simply don't define this:

B():host(A()) {}   // This is ugly and not needed !!

That is, the following should do what you want to do:

class B {
  B(const A& a): host(a) {}
private:
  //B():host(A()) {}   // This is ugly and not needed !!
  const A& host;
};

The idea is if you've defined a constructor that takes parameter(s), then the default constructor is not generated by the compiler. That means, instances of the above class cannot be default created!

 B b1; //error - needs default constructor which doesn't exist!
 B b2(a); //ok - only way to create an instance!

C++11 solution

In C++11, you can explicity tell the compiler not to generate a particular constructor as:

struct B
{
     B(const A &a) {}

     B() = delete;      //disable
};

Not only that. There is more to it, as explained below:

Now the interesting part

You can also selectively disable constructor(s) for selected types which makes delete more interesting. Consider this,

struct A
{
       A (int) {}
};

Object of this class can be created not only with int argument, but any type which implicitly converts to int. For example,

A a1(10);  //ok
A a2('x'); //ok - char can convert to int implicitly

B b; 
A a3(b); //ok - assume b provides user-defined conversion to int

Now suppose, for whatever reason, I don't want the users of class A to create objects with char or class B , which fortunately or unfortunately can implicitly convert to int, then you can disable them as:

struct A
{
     A(int) {}
     A(char) = delete;      //disable
     A(const B&) = delete;  //disable
};

Now here you go:

A a1(10);  //ok
A a2('x'); //error

B b; 
A a3(b); //error - assume (even if) b provides user-defined conversion to int

Online Demo : http://ideone.com/EQl5R

The error messages are very clear:

prog.cpp:9:5: error: deleted function 'A::A(char)'
prog.cpp:10:5: error: deleted function 'A::A(const B&)'

like image 116
Nawaz Avatar answered Oct 13 '22 01:10

Nawaz


Just leave it out. As soon as you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).

If you want to forbid any construction – ending up with a class that has only static members – you can simply declare the constructor as private, and not define it. Such a class is very rarely useful in C++ (since you cannot create instances of it); the only purpose that I can think of is to implement trait classes:

template <typename T>
struct type_to_color {
    static char const* value() { return "blue"; }

private:
    type_to_color();
};

template <>
struct type_to_color<int> {
    // Integers are red!
    static char const* value() { return "red"; }

private:
    type_to_color();
}

char const* char_color = type_to_color<char>::value();
char const* int_color  = type_to_color<int>::value();

However, this is extremely uncommon: trait classes are abundant in C++ but they never declare their constructors as private, it’s just assumed that everybody knows not to instantiate them.

like image 25
Konrad Rudolph Avatar answered Oct 13 '22 01:10

Konrad Rudolph