C++有像Pascal一样的“with”关键字吗?

26

with 关键字在 Pascal 中可以用来快速访问记录的字段。 有人知道C++中是否有类似的东西吗?

例如: 我有一个指针,它有很多字段,我不想像这样输入:

if (pointer->field1) && (pointer->field2) && ... (pointer->fieldn)

我真正想要的是C++中类似这样的东西:

with (pointer)
{
  if (field1) && (field2) && .......(fieldn)
}

2
哦,Javascript 有 with 关键字,它实际上做的事情基本相同;我没想到它的渊源可以追溯到 Pascal (!)。 - Ben Zotto
也许它来自Cobol或ADA,谁知道呢... - Svetlozar Angelov
在Ada中有一个with关键字,但不是那个意思。 - AProgrammer
在VB和VB.Net中都有一个with关键字,意思相同。 - Ignacio Soler Garcia
但在VB中,您需要说With obj / .X = x,因此它不太含糊。 - erikkallen
19个回答

28

也许你最接近的方法就是这样:(这只是一个学术练习。当然,你不能在这些人工 with 块的主体中使用任何局部变量!)

struct Bar {
    int field;
};

void foo( Bar &b ) {
    struct withbar : Bar { void operator()() {
        cerr << field << endl;
    }}; static_cast<withbar&>(b)();
}

或者,再恶魔一点说,
#define WITH(T) do { struct WITH : T { void operator()() {
#define ENDWITH(X) }}; static_cast<WITH&>((X))(); } while(0)

struct Bar {
    int field;
};

void foo( Bar &b ) {
    if ( 1+1 == 2 )
        WITH( Bar )
            cerr << field << endl;
        ENDWITH( b );
}

或者在C++0x中

#define WITH(X) do { auto P = &X; \
 struct WITH : typename decay< decltype(X) >::type { void operator()() {
#define ENDWITH }}; static_cast<WITH&>((*P))(); } while(0)

        WITH( b )
            cerr << field << endl;
        ENDWITH;

非常好 :). 我想提一下,对于我们这些使用Metrowerks编译器的人来说,这很可能不起作用(它在函数中处理结构体时表现不佳) - Chris Walton
@arke:真的吗?这令人惊讶,当Metrowerks成为Mac标准时,我很喜欢它。嗯,看起来他们失去了Howard Hinnant... - Potatoswatter
你可以使用静态变量。 - Alexandre C.
在C++17中,最好这样写:if(auto a=foo();true){/**/} - somebody4

22

没有这样的关键字。


16

我喜欢使用:

    #define BEGIN_WITH(x) { \
        auto &_ = x;

    #define END_WITH() }

例子:

    BEGIN_WITH(MyStructABC)
    _.a = 1;
    _.b = 2;
    _.c = 3;
    END_WITH()

10

在C++中,你可以将代码放入被指针引用的类的方法中。这样你就可以直接引用成员而不使用指针。将其设为内联函数,你就能够得到想要的结果。


“make it inline” 是什么意思?你能在另一个函数内部声明这个方法吗?这很奇怪... - Dacav
1
@Dacav 这个答案只是建议在 with 中使用的类中添加一个方法。将其设置为 inline 允许它进入头文件,但除此之外没有其他的效果。这样的一个方法必须在它的原始类中声明,并在任何其他函数之外定义。我下面的答案有一个解决这两个问题的方法。 - Potatoswatter

9
尽管我主要使用的是Delphi编程语言,它具有一个"with"关键字(因为Delphi是Pascal的派生版本),但我不使用"with"。正如其他人所说:虽然可以节省一些输入,但阅读变得更加困难。
在像下面的代码中这种情况可能会引起使用"with"的诱惑:
cxGrid.DBTableView.ViewData.Records.FieldByName('foo').Value = 1;
cxGrid.DBTableView.ViewData.Records.FieldByName('bar').Value = 2;
cxGrid.DBTableView.ViewData.Records.FieldByName('baz').Value = 3;

使用 with 的代码如下:
with cxGrid.DBTableView.ViewData.Records do
begin
  FieldByName('foo').Value = 1;
  FieldByName('bar').Value = 2;
  FieldByName('baz').Value = 3;
end;

我更喜欢使用一种不同的技术,通过引入一个额外的变量来指向与with所指向的相同对象。就像这样:

var lRecords: TDataSet;

lRecords := cxGrid.DBTableView.ViewData.Records;

lRecords.FieldByName('foo').Value = 1;
lRecords.FieldByName('bar').Value = 2;
lRecords.FieldByName('baz').Value = 3;

这样可以避免歧义,缩短输入时间,并使代码的意图比使用 with 更清晰。


4
这种策略在 C++ 中也适用,使用本地的 T&T const& 变量来保存对长表达式的引用(在 T& 情况下,必须是一个适当的左值)。为了使其视觉上更加突出,我通常将变量命名为 _ - j_random_hacker

6
不,C++没有这样的关键字。

6

你能够接近的最佳方式是方法链

myObj->setX(x)
     ->setY(y)
     ->setZ(z)

用于设置多个字段的 set 方法和用于命名空间的 using 方法。


这真的非常好,因为封装性得到了保留...感谢您进行连接! - NicoBerrogorry
这绝对不是一回事! - Gabriel

5

C++没有类似的特性。而且许多人认为Pascal中的"WITH"是一个问题,因为它可能会使代码模糊和难以阅读,例如很难知道field1是指针的成员还是局部变量或其他什么东西。Pascal还允许多个with变量,例如"With Var1,Var2",这使得情况更加复杂。


5
如果某事物可以被滥用,就意味着它是不好的,这总是一个问题。 - Marco van de Voort
4
当涉及到C++中的名称查找时,情况并不那么简单明了。尤其是当涉及到函数的参数相关查找以及函数模板的实例化时,“可能使代码产生歧义”的论点并不成立,因此我不会给这个回答打负分。 - j_random_hacker
我同意它可能很有用,但我已经看到它被滥用了很多次,所以我避免使用它。特别是如果记录中的field1被重命名,那么你的with代码现在突然会悄悄地引用作用域中具有相同名称的另一个变量。我不使用with,而是使用这里建议的其他方法之一:使用短名称的本地变量或将其移动到函数/方法中。 - Ville Krumlinde
点赞。最糟糕的语言特性。考虑这个可怕的东西:使用struct1,struct2.substruct3,struct4.substruct5.ptrstruct6^...看到过吧。 - Nikola Gedelovski
4
Visual Basic通过要求属于'with'的字段前缀为“.”来整理语法-它还帮助了语法完成等。我们的主持人Joel做到了这一点 :) - Will
只有应用于单一类型(记录、结构、对象等),才不会有危险。换句话说,永远不要在“with”语句中混合对象,这样就安全了。 - Gabriel

3
不,C/C++中没有with关键字。
但是你可以通过一些预处理器代码添加它:
/* Copyright (C) 2018 Piotr Henryk Dabrowski, Creative Commons CC-BY 3.0 */

#define __M2(zero, a1, a2, macro, ...) macro

#define __with2(object, as) \
    for (typeof(object) &as = (object), *__i = 0; __i < (void*)1; ++__i)

#define __with1(object) __with2(object, it)

#define with(...) \
    __M2(0, ##__VA_ARGS__, __with2(__VA_ARGS__), __with1(__VA_ARGS__))

使用方法:

with (someVeryLongObjectNameOrGetterResultOrWhatever) {
    if (it)
        it->...
    ...
}

with (someVeryLongObjectNameOrGetterResultOrWhatever, myObject) {
    if (myObject)
        myObject->...
    ...
}

简化的非重载定义(选择一个):

未命名(Kotlin 风格 it):

#define with(object) \
    for (typeof(object) &it = (object), *__i = 0; __i < (void*)1; ++__i)

命名为:

#define with(object, as) \
    for (typeof(object) &as = (object), *__i = 0; __i < (void*)1; ++__i)

当然,for循环总是只有一次遍历,并且将被编译器优化掉。

3
with (OBJECT) {CODE}

C++中不存在这种东西。
你可以将代码放在对象的方法中,但这并不总是理想的。

使用C++11,您可以通过为对象创建短名称别名来实现相当接近。
例如,在问题中给出的代码将如下所示:

{
    auto &_ = *pointer;
    if (_.field1 && ... && _.fieldn) {...}
}

如果您经常使用某个字段,可以直接使用别名来简化操作:

周围的花括号用于限制别名_的可见性。

auto &field = pointer->field;
// Even shorter alias:
auto &_ = pointer->busy_field;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接