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

4

6

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.

Buckman answered 6/9, 2011 at 9:13 Comment(4)
@Jon: Oh man, great! I got into the bad habbit of putting the implementation directly into the class definition. Declaring but not defining the constructor does the trick! Many Thanks!Buckman
@Mat Hmm.. What did you edit in my question (and why)?Buckman
click on the edited x minutes ago link to see your question's edit history. I just expanded ctor into a full word to match the rest of your text. You can click on the "rollback" link in there if you want ctor back.Spray
Ok, this question was answered (to more than I would have asked for) Many thanks to all! Jon & Nawaz were the first. Jon deleted his answer. So, I guess he doesn't mind the point and this point goes to Nawaz. However, many thanks for the c++11 solutions from MartinhnoBuckman
P
14

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&)'

Peterson answered 6/9, 2011 at 9:18 Comment(3)
You could add that to disallow a constructor, one just need to declare it private (no need to define it). This could be useful to disallow the copy constructor, for example.Corselet
@Nawaz That's pretty clever! One must remember this rule when reading code. You think its good habit to rely on this rule and, say, omit the declaration at all instead of just declaring it without defining it?Buckman
There even more to it. The deleted char overload is a candidate for other types, like unsigned, so now only construction with int succeeds. ideone.com/r9DD8 (the error message is bizarre, though: where's the ambiguity if only one candidate exists :) )Washrag
C
11

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.

Carillon answered 6/9, 2011 at 9:18 Comment(3)
You could add that to disallow a constructor, one just need to declare it private (no need to define it). This could be useful to disallow the copy constructor, for example.Corselet
@Luc This use-case doesn’t really exist in C++. I’ve added an artificial example (trait class) but you’d never delete their constructor in normal usage.Carillon
I never mentioned disallowing all constructors, just some of them. In the code provided in the question, the copy constructor is still generated. If the OP wants to disallow it, he must declare it private but he does not need to define it (otherwise, he would have the same issue he described, namely having to initialize the member A.Corselet
R
2

I'll post the C++11 solution: delete the constructor.

class B {
  B() = delete;
  B(const A& a): host(a) {}
private:
  const A& host;
};
Rentschler answered 6/9, 2011 at 9:29 Comment(2)
Interesting. But isn't the default constructor deleted anyway when declaring another constructor with parameters ?Blessington
@ereOn: Yes, that's true. Making it explicit may help making it clear that you didn't forget that and really don't want one, but it's not necessary.Rentschler
U
0

As Konrad Rudolph sayd: as soon you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).

Therefore, other options are:

Declare the constructor private (so that you can't inherit from your class), but do not provide a definition:

class B {
public:
  B(const A& a): host(a) {}
private:
  B(); // not implemented!
  const A& host;
};

Or in C++11, as R. Martinho Fernandes says:

class B {
public:
  B() = delete;
  B(const A& a): host(a) {}
private:
  const A& host;
};
Underset answered 6/9, 2011 at 9:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.