How to disallow temporaries

后端 未结 10 1690
一个人的身影
一个人的身影 2020-12-02 05:19

For a class Foo, is there a way to disallow constructing it without giving it a name?

For example:

Foo(\"hi\");

And only allow it i

相关标签:
10条回答
  • 2020-12-02 05:56

    This one doesn't result in a compiler error, but a runtime error. Instead of measuring a wrong time, you get an exception which may be acceptable too.

    Any constructor you want to guard needs a default argument on which set(guard) is called.

    struct Guard {
      Guard()
        :guardflagp()
      { }
    
      ~Guard() {
        assert(guardflagp && "Forgot to call guard?");
        *guardflagp = 0;
      }
    
      void *set(Guard const *&guardflag) {
        if(guardflagp) {
          *guardflagp = 0;
        }
    
        guardflagp = &guardflag;
        *guardflagp = this;
      }
    
    private:
      Guard const **guardflagp;
    };
    
    class Foo {
    public:
      Foo(const char *arg1, Guard &&g = Guard()) 
        :guard()
      { g.set(guard); }
    
      ~Foo() {
        assert(!guard && "A Foo object cannot be temporary!");
      }
    
    private:
      mutable Guard const *guard;
    }; 
    

    The characteristics are:

    Foo f() {
      // OK (no temporary)
      Foo f1("hello");
    
      // may throw (may introduce a temporary on behalf of the compiler)
      Foo f2 = "hello";
    
      // may throw (introduces a temporary that may be optimized away
      Foo f3 = Foo("hello");
    
      // OK (no temporary)
      Foo f4{"hello"};
    
      // OK (no temporary)
      Foo f = { "hello" };
    
      // always throws
      Foo("hello");
    
      // OK (normal copy)
      return f;
    
      // may throw (may introduce a temporary on behalf of the compiler)
      return "hello";
    
      // OK (initialized temporary lives longer than its initializers)
      return { "hello" };
    }
    
    int main() {
      // OK (it's f that created the temporary in its body)
      f();
    
      // OK (normal copy)
      Foo g1(f());
    
      // OK (normal copy)
      Foo g2 = f();
    }
    

    The case of f2, f3 and the return of "hello" may not be wanted. To prevent throwing, you can allow the source of a copy to be a temporary, by resetting the guard to now guard us instead of the source of the copy. Now you also see why we used the pointers above - it allows us to be flexible.

    class Foo {
    public:
      Foo(const char *arg1, Guard &&g = Guard()) 
        :guard()
      { g.set(guard); }
    
      Foo(Foo &&other)
        :guard(other.guard)
      {
        if(guard) {
          guard->set(guard);
        }
      }
    
      Foo(const Foo& other)
        :guard(other.guard)
      {
        if(guard) {
          guard->set(guard);
        }
      }
    
      ~Foo() {
        assert(!guard && "A Foo object cannot be temporary!");
      }
    
    private:
      mutable Guard const *guard;
    }; 
    

    The characteristics for f2, f3 and for return "hello" are now always // OK.

    0 讨论(0)
  • 2020-12-02 06:02

    Declare one-parametric constructor as explicit and nobody will ever create an object of that class unintentionally.

    For example

    class Foo
    {
    public: 
      explicit Foo(const char*);
    };
    
    void fun(const Foo&);
    

    can only be used this way

    void g() {
      Foo a("text");
      fun(a);
    }
    

    but never this way (through a temporary on the stack)

    void g() {
      fun("text");
    }
    

    See also: Alexandrescu, C++ Coding Standards, Item 40.

    0 讨论(0)
  • 2020-12-02 06:05

    As is, with your implementation, you cannot do this, but you can use this rule to your advantage:

    Temporary objects cannot be bound to non-const references

    You can move the code from the class to an freestanding function which takes a non-const reference parameter. If you do so, You will get a compiler error if an temporary tries to bind to the non-const reference.

    Code Sample

    class Foo
    {
        public:
            Foo(const char* ){}
            friend void InitMethod(Foo& obj);
    };
    
    void InitMethod(Foo& obj){}
    
    int main()
    {
        Foo myVar("InitMe");
        InitMethod(myVar);    //Works
    
        InitMethod("InitMe"); //Does not work  
        return 0;
    }
    

    Output

    prog.cpp: In function ‘int main()’:
    prog.cpp:13: error: invalid initialization of non-const reference of type ‘Foo&’ from a temporary of type ‘const char*’
    prog.cpp:7: error: in passing argument 1 of ‘void InitMethod(Foo&)’
    
    0 讨论(0)
  • 2020-12-02 06:06

    No, I'm afraid this isn't possible. But you could get the same effect by creating a macro.

    #define FOO(x) Foo _foo(x)
    

    With this in place, you can just write FOO(x) instead of Foo my_foo(x).

    0 讨论(0)
提交回复
热议问题