3

Suppose i have a non-copyable class Foo, and one of its constructors just happens to receive a reference to Foo.

class Foo
{
public:
    Foo(Foo& parent) {...}

private:
    void operator=(Foo); // disabled
    ...
};

The compiler thinks that this is a copy-constructor, while it does something entirely unrelated to copying (so the assignment operator is disabled).

Is there any danger in defining the constructor this way, or should i change its signature artificially, e.g. use pointer instead of reference, or add a mandatory dummy parameter?

Here is some context (probably not required to understand/answer my question).

I have a class library that i have written myself, which serves as a connection between user code and another library. The other library provides a service that i call frobnicate for brevity. User code can look like this:

class UsefulObject: public mylib::Frobnicator
{
    ...
    void DoStuff()
    {
        int x = ...
        ...
        frobnicate(x); // it's important to allow simple syntax here
        frobnicate(x + 1);
        ...
    }
    ...
};

I want to support an hierarchy of user objects: each object is contained in another (its parent), while there are a few (in my case, 5) top-level objects that contain every other objects.

Each object has a log-file; i want each call to be logged in several log files, up the containment hierarchy until a top-level object.

I have it implemented this way:

namespace mylib
{
    class Frobnicator // provides the frobnication service
    {
    public:
        Frobnicator(Frobnicator& parent): parent(parent) {}
    protected:
        virtual void frobnicate(int x) {
            ... // some logging code
            parent->frobnicate(x);
        }
    private:
        Frobnicator& parent;
    };

    namespace internal // users of mylib, please don't use this!
    {
        class TheUltimateFrobnicator: public Frobnicator
        {
        protected:
            virtual void frobnicate(int x) {
                the_other_library::frobnicate(x);
            }
        private:
            TheUltimateFrobnicator(int id); // called by a factory or some such
        };
    }
}
anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • "The compiler thinks that this is a copy-constructor". Well, by definition, it is a copy-constructor even if you don't want it to do copying. – CB Bailey Jan 23 '11 at 15:11

1 Answers1

7

This appears to be an unfortunate ambiguity in the meaning of such a constructor. I think the most straight forward way to disambiguate this is to use a pointer

class Frobnicator // provides the frobnication service
{
public:
    explicit Frobnicator(Frobnicator *parent): parent(parent) {}
protected:
    virtual void frobnicate(int x) {
        ... // some logging code
        parent->frobnicate(x);
    }
private:
    void operator=(Foo); // disabled
    Frobnicator(Frobnicator const&); // disabled

    Frobnicator *parent;
};

I know a lot of people who would expect the parameter to be a pointer anyway. Incidentally, you yourself mistyped parent.frobnicate as parent->frobnicate.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    Wouldn't declaring the constructor `explicit` help here? – Péter Török Jan 23 '11 at 14:24
  • great :-) How about the original case? Wouldn't an `explicit Frobnicator(Frobnicator& parent)` prevent the compiler from misusing it? – Péter Török Jan 23 '11 at 14:33
  • @Peter it won't prevent TheUltimateFrobnicator from misusing it. Base class constructor calls will also consider explicit constructors. But apart from that, it strikes me as ugly that `Frobnicator f = g;` will fail, but that `Frobnicator f(g)` will work. I really cannot distinguish between them in a way like "one copies and the other doesn't". – Johannes Schaub - litb Jan 23 '11 at 14:34