如何禁止临时

时间:2012-10-31 14:00:39

标签: c++

对于Foo类,有没有办法在不给它命名的情况下禁止构造它?

例如:

Foo("hi");

如果你给它一个名字,只允许它,如下所示?

Foo my_foo("hi");

第一个的生命周期只是语句,第二个是封闭块。在我的用例中,Foo正在测量构造函数和析构函数之间的时间。因为我从不参考局部变量,所以我常常忘记将其放入,并且意外地改变了生命周期。我想改为编译错误。

10 个答案:

答案 0 :(得分:101)

另一种基于宏的解决方案:

#define Foo class Foo

声明Foo("hi");扩展为class Foo("hi");,这是不正确的;但Foo a("hi")扩展为class Foo a("hi"),这是正确的。

这样做的优点是它与现有(正确)代码的源代码和二进制兼容。 (这个说法并不完全正确 - 请参阅Johannes Schaub的评论以及随后的讨论:“你怎么能知道它与现有代码的源代码兼容?他的朋友包括他的标题并且有空f(){ int Foo = 0;}之前已编译好并且现在编译错误!此外,定义类Foo的成员函数的每一行都失败:void class Foo :: bar(){}“

答案 1 :(得分:71)

一点点破解

class Foo
{
    public:
        Foo (const char*) {}
};

void Foo (float);


int main ()
{
    Foo ("hello"); // error
    class Foo a("hi"); // OK
    return 1;
}

答案 2 :(得分:45)

将构造函数设为私有,但为类提供 create 方法。

答案 3 :(得分:25)

这个不会导致编译器错误,但会导致运行时错误。而不是测量错误的时间,你得到一个也可以接受的例外。

您想要保护的任何构造函数都需要一个调用set(guard)的默认参数。

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;
}; 

特征是:

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();
}

可能不需要f2f3"hello"的返回。为了防止投掷,您可以通过重置guard来保护我们而不是副本的来源,从而允许副本的来源是临时的。现在你也看到了为什么我们使用上面的指针 - 它允许我们灵活。

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;
}; 

f2f3return "hello"的特征现在始终为// OK

答案 4 :(得分:18)

几年前,我为GNU C ++编译器写了a patch,为这种情况添加了一个新的警告选项。这在a Bugzilla item中进行了跟踪。

不幸的是,GCC Bugzilla是一个埋葬地,其中考虑周全的补丁包含的功能建议将会消失。 :)

这是因为希望在代码中捕获这个问题主题的错误,这些错误使用本地对象作为锁定和解锁的小工具,测量执行时间等等。

答案 5 :(得分:9)

按原样,您的实施不能这样做,但您可以利用此规则:

  

临时对象不能绑定到非const引用

您可以将代码从类移动到带有非const引用参数的独立函数。如果这样做,如果临时尝试绑定到非const引用,您将收到编译器错误。

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;
}

输出

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

答案 6 :(得分:7)

根本没有默认构造函数,并且需要在每个构造函数中引用一个实例。

#include <iostream>
using namespace std;

enum SelfRef { selfRef };

struct S
{
    S( SelfRef, S const & ) {}
};

int main()
{
    S a( selfRef, a );
}

答案 7 :(得分:6)

不,我担心这是不可能的。但是你可以通过创建宏来获得相同的效果。

#define FOO(x) Foo _foo(x)

有了这个,你可以写FOO(x)而不是Foo my_foo(x)。

答案 8 :(得分:4)

由于主要目标是防止错误,请考虑以下事项:

struct Foo
{
  Foo( const char* ) { /* ... */ }
};

enum { Foo };

int main()
{
  struct Foo foo( "hi" ); // OK
  struct Foo( "hi" ); // fail
  Foo foo( "hi" ); // fail
  Foo( "hi" ); // fail
}

这样你就不会忘记给变量命名,你不能忘记写struct。详细,但安全。

答案 9 :(得分:1)

将one-parametric构造函数声明为显式,并且没有人会无意中创建该类的对象。

例如

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

void fun(const Foo&);

只能以这种方式使用

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

但从未这样(通过临时堆叠)

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

参见:Alexandrescu,C ++编码标准,第40项。