Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/136.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何禁止临时工_C++ - Fatal编程技术网

C++ 如何禁止临时工

C++ 如何禁止临时工,c++,C++,对于类Foo,有没有一种方法不允许在不给它命名的情况下构造它 例如: Foo("hi"); 只有你给它起了个名字才允许它,像下面这样 Foo my_foo("hi"); 第一个语句的生存期就是语句,第二个语句的生存期就是封闭块。在我的用例中,Foo正在测量构造函数和析构函数之间的时间。由于我从不引用局部变量,我经常忘记将其放入,并意外地更改了生存期。我想得到一个编译时错误。不,恐怕这是不可能的。但是你可以通过创建一个宏来获得同样的效果 #define FOO(x) Foo _foo(x)

对于类Foo,有没有一种方法不允许在不给它命名的情况下构造它

例如:

Foo("hi");
只有你给它起了个名字才允许它,像下面这样

Foo my_foo("hi");

第一个语句的生存期就是语句,第二个语句的生存期就是封闭块。在我的用例中,
Foo
正在测量构造函数和析构函数之间的时间。由于我从不引用局部变量,我经常忘记将其放入,并意外地更改了生存期。我想得到一个编译时错误。

不,恐怕这是不可能的。但是你可以通过创建一个宏来获得同样的效果

#define FOO(x) Foo _foo(x)

有了它,你就可以写FOO(x)而不是FOO my_FOO(x)。

稍微修改一下怎么样

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

void Foo (float);


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

实际上,在您的实现中,您无法做到这一点,但您可以使用此规则为您带来好处:

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

您可以将代码从类移动到采用非常量引用参数的独立函数。如果执行此操作,则如果临时对象尝试绑定到非常量引用,则会出现编译器错误

输出
另一个基于宏的解决方案:

#define Foo class Foo
声明
Foo(“hi”)扩展到
类Foo(“hi”),格式不正确;但是
fooa(“hi”)
扩展到
类fooa(“hi”)
,这是正确的


这样做的优点是,它与现有(正确的)代码同时兼容源代码和二进制代码。(这种说法并不完全正确——请参见Johannes Schaub的评论和下面的讨论:“你怎么知道它与现有代码是源代码兼容的?他的朋友包括了他的头,并且有void f(){int Foo=0;}以前定义好类和现在的错误编译!而且,定义类FO的成员函数的每一行失败:空类Fo::Bar()({})

< P>使构造器私有但给类A创建方法。

< P>几年前我为GNU C++编译器编写了一个新的警告选项。这是跟踪到的

不幸的是,GCC Bugzilla是一个墓地,在这里,经过深思熟虑的补丁(包括功能建议)将消亡


其动机是希望在使用本地对象作为小工具进行锁定和解锁、测量执行时间等的代码中准确捕获这类问题所涉及的bug。

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

要保护的任何构造函数都需要一个默认参数,在此参数上调用
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();
}
可能不希望出现
f2
f3
返回“hello”
的情况。为了防止抛出,您可以通过将
保护重置为现在保护我们而不是副本的源,从而允许副本的源是临时的。现在你也明白为什么我们使用上面的指针了——它让我们变得灵活

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

f2
f3
返回“hello”
的特征现在总是
//OK

只是没有默认的构造函数,而是需要在每个构造函数中引用实例

#include <iostream>
using namespace std;

enum SelfRef { selfRef };

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

int main()
{
    S a( selfRef, a );
}
#包括
使用名称空间std;
枚举SelfRef{SelfRef};
结构
{
S(SelfRef,S常数&){
};
int main()
{
S a(selfRef,a);
}

将一个参数化构造函数声明为显式,没有人会无意中创建该类的对象

比如说

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

void fun(const Foo&);
只能这样使用

void g() {
  Foo a("text");
  fun(a);
}
但决不能这样(通过堆栈上的临时文件)


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

< P>因为主要目标是防止错误,请考虑如下:

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
。这是一个很好的例子,但是对于你来说,你可以编写自己的C++编译器,但是它严格来说是C++。在某些地方,这样的临时变量也很有用,比如从函数返回对象时(比如
return std::string(“Foo”);
)不,你不能这样做,很抱歉,根据你的宗教信仰,这可能是宏可以派上用场的情况(通过usnig该类型只能通过一个总是创建变量的宏)看起来更像是我希望我的LINT工具捕捉的东西,而不是我希望通过编译器黑客从语法上阻止的东西。我本来打算向上投票,但后来我看到了“你可以创建一个宏”。好吧,修复了下划线。@Griwes-不要成为一个原教旨主义者。最好说“使用宏”比“这不可能做到”。好吧,这不可能做到。你根本没有解决这个问题,做
Foo()仍然是完全合法的
。现在你在这里很固执。将Foo类重命名为一个复杂的名称,并将宏称为Foo。问题解决了。类似于:
类不直接使用这个类,只通过\u Foo\u宏使用它;
很棒的Hack!注意:
Foo a(“hi”)
(没有
)这也是一个错误。我不确定我是否理解。Foo(“你好”)尝试调用void Foo(float)它会导致一个链接器错误?但是为什么要调用float版本而不是Foo-ctor?undu,嗯,你使用的是什么编译器?gcc 3.4抱怨没有到float的转换。它试图调用一个函数
Foo
,因为它优先于一个类。@aleguna实际上我没有尝试运行这段代码,那只是一个错误猜猜:S,但是你回答了我的问题,我不知道函数优先于类。@ didieC.NO,<代码> fo::Foo(“Hi”)< /C> >在C++中被禁止。@ didieRC:只要它们提供了一个附加的函数,就由你不这样做。我们试图调整一种方法来达到标准中没有明确允许的东西,当然。
void g() {
  Foo a("text");
  fun(a);
}
void g() {
  fun("text");
}
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
}