是否可以在for循环中声明两个不同类型的变量?


240

是否可以在C ++中的for循环的初始化主体中声明两个不同类型的变量?

例如:

for(int i=0,j=0 ...

定义两个整数。我可以在初始化正文中定义an int和a char吗?怎么做?


3
在g ++-4.4(-std=c++0x)中可能以的形式出现for(auto i=0, j=0.0; ...,但这种可能性在g ++-4.5中已删除,与c ++ 0x文本一致。
rafak 2010年

Answers:


133

C ++ 17是的!您应该使用结构化绑定声明。多年来,gcc和clang支持该语法(自gcc-7和clang-4.0起)(clang live示例)。这使我们可以像这样拆开元组的包装:

for (auto [i, f, s] = std::tuple{1, 1.0, std::string{"ab"}}; i < N; ++i, f += 1.5) {
    // ...
}

以上将为您提供:

  • int i 调成 1
  • double f 调成 1.0
  • std::string s 调成 "ab"

确保#include <tuple>进行此类声明。

如果要命名类型,可以tuple通过键入全部内容来指定内的确切类型std::string。例如:

auto [vec, i32] = std::tuple{std::vector<int>{3, 4, 5}, std::int32_t{12}}

具体的应用是遍历地图,获取键和值,

std::unordered_map<K, V> m = { /*...*/ };
for (auto& [key, value] : m) {
   // ...
}

在这里查看现场示例


C ++ 14:您可以通过添加基于类型来与C ++ 11(下文)相同std::get。因此std::get<0>(t),您可以拥有而不是下面的示例std::get<int>(t)


C ++ 11std::make_pair允许您以及std::make_tuple两个以上的对象执行此操作。

for (auto p = std::make_pair(5, std::string("Hello World")); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

std::make_pair将在中返回两个参数std::pair。可以使用.first和来访问元素.second

对于两个以上的对象,您需要使用 std::tuple

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    std::cout << std::get<1>(t) << std::endl; // cout Hello world
    std::get<2>(t).push_back(std::get<0>(t)); // add counter value to the vector
}

std::make_tuple是一个可变参数模板,它将构造任意数量的参数的元组(当然有一些技术限制)。元素可以通过索引访问std::get<INDEX>(tuple_object)

在for循环体内,您仍然可以轻松地为对象加上别名,尽管您仍然需要使用for .firststd::getfor循环条件并更新表达式

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    auto& i = std::get<0>(t);
    auto& s = std::get<1>(t);
    auto& v = std::get<2>(t);
    std::cout << s << std::endl; // cout Hello world
    v.push_back(i); // add counter value to the vector
}

C ++ 98和C ++ 03您可以显式命名std::pair。但是,没有标准的方法可以将其概括为两种以上的类型:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

5
如果您使用的是C ++ 17,甚至可以删除make_和编写代码std::pair(1, 1.0)
Marc Glisse '16

毛茸茸的C ++ 14样式元组/结对业务-都不错(可能是赞成),但看起来很奇怪:)
mlvljr

3
简而言之:是可以的,但是不会很漂亮。
一些程序员花花公子

是的,不漂亮,但是很浓!绝对喜欢元组式的。:)但这实际上是C ++中for循环的一种非常直观的句法特性,让我头痛了半个多小时才终于意识到必须使用Google进行搜索……
aderchox

@aderchox,如果您能澄清您的误会,我可以更新答案
Ryan Haining

276

否-但是从技术上讲,有一种解决方法(除非强制执行,否则我实际上不会使用它):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}

3
这不会在VS 2008上进行编译,但会在Comeau在线上进行;-)
JRL,2010年

7
@JRL:哦,VS2005也没有。我猜想,VC ++中还有另一个不合规的功能。
Georg Fritzsche 2010年

3
我已经在Perl中完成了等效的工作。不过,我还没有尝试通过C ++中的代码审查来潜入类似内容。
约翰

21
使用c ++ 11,您可以使用默认值使此示例更短struct { int a=0; char b='a'; } s;
Ryan Haining

1
该答案满足了答案的要求,但是从可读性的角度来看,我更喜欢@MK。的答案。MK的解决方案甚至通过添加花括号来解决范围问题。
Trevor Boyd Smith,

221

不可能,但是您可以执行以下操作:

float f;
int i;
for (i = 0,f = 0.0; i < 5; i++)
{
  //...
}

或者,明确限制范围fi使用其他括号:

{
    float f; 
    int i;
    for (i = 0,f = 0.0; i < 5; i++)
    {
       //...
    }
}

我知道这是一个非常老的问题,但是您能否解释为什么有些人会像在您的第二个示例中那样用它周围的括号括起来?
2013年

13
@fizzisist明确地将f和i的范围限制为仅使用它们的部分代码。
MK。

1
@MK。谢谢,这就是我所怀疑的。我编辑了您的答案以解释这一点。
2013年

只有一个问题:为什么会这样?:O
rohan-patel

因为它的工作方式类似于'int a = 0,b = 4'。话虽这么说,对f和i进行作用域划分可能仅对防止重用这些名称有用(这是合理的原因),但是在现代编译器上,生成的代码通常是相同的(在这种情况下)。
阿苏

14

您不能在初始化中声明多种类型,但可以将多种类型分配给EG

{
   int i;
   char x;
   for(i = 0, x = 'p'; ...){
      ...
   }
}

只需在自己的范围内声明它们即可。


3

我认为最好的方法是西安的答案

但...


#嵌套循环

这种方法很脏,但是可以解决所有版本。

因此,我经常在宏函数中使用它。

for(int _int=0, /* make local variable */ \
    loopOnce=true; loopOnce==true; loopOnce=false)

    for(char _char=0; _char<3; _char++)
    {
        // do anything with
        // _int, _char
    }

附加1。

它也可以用于declare local variablesinitialize global variables

float globalFloat;

for(int localInt=0, /* decalre local variable */ \
    _=1;_;_=0)

    for(globalFloat=2.f; localInt<3; localInt++) /* initialize global variable */
    {
        // do.
    }

附加2。

很好的例子:具有宏功能。

(如果因为是循环宏而无法使用最佳方法

#define for_two_decl(_decl_1, _decl_2, cond, incr) \
for(_decl_1, _=1;_;_=0)\
    for(_decl_2; (cond); (incr))


    for_two_decl(int i=0, char c=0, i<3, i++)
    {
        // your body with
        // i, c
    }

#if陈述技巧

if (A* a=nullptr);
else
    for(...) // a is visible

如果要初始化为0nullptr,则可以使用此技巧。

但由于阅读困难,我不建议您这样做。

好像是虫子。


它永远不会令我惊讶,有些人的想法与其他人有何不同。我永远也不会想到这种奇怪的事情。有趣的想法。
Person Person II博士

1

请参阅“ 是否可以在for循环中定义两种类型的变量?涉及嵌套多个for循环的另一种方法, ”。与Georg的“结构技巧”相比,另一种方法的优势在于,它(1)允许您混合使用静态和非静态局部变量,并且(2)允许您具有不可复制的变量。不利的一面是它的可读性和效率可能大大降低。


-2

定义一个宏:

#define FOR( typeX,x,valueX,  typeY,y,valueY,  condition, increments) typeX x; typeY y; for(x=valueX,y=valueY;condition;increments)

FOR(int,i,0,  int,f,0.0,  i < 5, i++)
{
  //...
}

请记住,您的变量作用域也不会以这种方式位于for循环内。


通过使用{和将代码包装在宏中的单独范围中,可以轻松克服该限制}
内森·奥斯曼

4
不,他不能。他的宏不会包装循环体。他可以添加一个额外的开括号,但是在使用宏时将需要一个“额外的”关闭括号。
约翰

3
这是一个有趣的想法,但是在考虑之前,我会尽快使用其他答案。
gregn3

-2

您也可以在C ++中像下面这样使用。

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.