基类初始化器列表在for循环中

huangapple go评论83阅读模式
英文:

Base class initializer list in for loop

问题

有没有更好/更清晰的方法来做到这一点?

class Base {};
class Derived1 : Base {};
class Derived2 : Base {};

//================================

Derived1 var1, var2;
Derived2 var3, var4;

for (auto& var : {var1, var2, var3, var4}) {
    ...
}
英文:

Anyone have a better/cleaner way to do this?

class Base {};
class Derived1 : Base {};
class Derived2 : Base {};

//================================

Derived1 var1, var2;
Derived2 var3, var4;

for (auto* var : std::initializer_list<Base*>{&var1, &var2, &var3, &var4}) {
    ...
}

Would like to avoid having to explicitly type the initializer_list and, for bonus points, using references and not having to resort to pointers.

答案1

得分: 3

唯一的替代方式,但这可能不是您要找的方式:

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

Derived1 var1, var2;
Derived2 var3, var4;

Base* objects[] = { &var1, &var2, &var3, &var4 };

for (auto* var : objects) {
    ...
}
英文:

The only alternate way, but it's probably not what you are looking for:

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

Derived1 var1, var2;
Derived2 var3, var4;

Base* objects[] = { &var1, &var2, &var3, &var4 };

for (auto* var : objects) {
    ...
}

答案2

得分: 1

你无法自动推断Base。而且,甚至没有将Derived1*Derived2*隐式转换为Base*,因为你使用了一个私有基类。

我假设你实际上想要在以下代码中使用公共基类

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

你无法推断出Derived1Derived2的公共基类,因此如果要使用任何可迭代的内容,你需要手动指定类型。

但是,你可以创建一个模板函数,带有参数包,以执行每个传递参数的函数器,或者创建一个可迭代对象,在解引用迭代器时返回对Base的引用。

在以下代码中,我展示了这两种选项,但为了简单起见,我使用了std::array,其中包含std::reference_wrapper<Base>,而不是实现自定义集合,该集合具有返回对Base的引用的迭代器。

template<class F, class... Args>
void ForEach(F&& f, Args&&... args)
{
    ((f(std::forward<Args>(args))), ...);
}

void printType(Derived1 const&)
{
    std::cout << "Derived1\n";
}

void printType(Derived2 const&)
{
    std::cout << "Derived2\n";
}

void printType(Base const&)
{
    std::cout << "Base\n";
}

template<class T, class ...Args>
std::array<std::reference_wrapper<T>, sizeof...(Args)> AsIterable(Args& ...args)
{
    return {args...};
}

int main()
{
    Derived1 var1, var2;
    Derived2 var3, var4;

    std::cout << "generic lambda:\n";

    ForEach([](auto& x)
        {
            printType(x);
        }, var1, var2, var3, var4);


    std::cout << "lambda with typed parameter:\n";

    ForEach([](Base& x)
        {
            printType(x);
        }, var1, var2, var3, var4);

    std::cout << "range-based for loop with reference wrapper array:\n";

    for (auto& var : AsIterable<Base>(var1, var2, var3, var4))
    {
        // 注意:var的类型是std::reference_wrapper<Base>&,而不是上面提到的Base&
        printType(var);
    }

    std::cout << "pointer values(main):\n"
        << &var1 << '\n'
        << &var2 << '\n'
        << &var3 << '\n'
        << &var4 << '\n'
        ;

    std::cout << "pointer values(lambda):\n";
    ForEach([](Base& x)
        {
            std::cout << &x << '\n';
        }, var1, var2, var3, var4);

    std::cout << "pointer values(range-based for):\n";
    for (auto& var : AsIterable<Base>(var1, var2, var3, var4))
    {
        std::cout << &var.get() << '\n';
    }
}
英文:

You cannot automatically deduce Base. Furthermore there isn't even an implicit conversion of Derived1* or Derived2* to Base*, since you're using a private base class.

I'm assuming you actually wanted public base classes in the following code

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

.

You cannot deduce the common base class of both Derived1 and Derived2, so the type actually needs to be mentioned, if you want to use anything that's iterable, you need to manually specify the type.

However you could create a template function with a parameter pack to either execute a functor for every parameter passed or to create something that is an iterable that returns references to Base when dereferencing the iterator.

In the following I show both options, but for simplicity's sake I go with an std::array of std::reference_wrapper&lt;Base&gt; instead of implementing custom collection that has an iterator returning references to Base.

template&lt;class F, class...Args&gt;
void ForEach(F&amp;&amp; f, Args&amp;&amp;...args)
{
((f(std::forward&lt;Args&gt;(args))), ...);
}
void printType(Derived1 const&amp;)
{
std::cout &lt;&lt; &quot;Derived1\n&quot;;
}
void printType(Derived2 const&amp;)
{
std::cout &lt;&lt; &quot;Derived2\n&quot;;
}
void printType(Base const&amp;)
{
std::cout &lt;&lt; &quot;Base\n&quot;;
}
template&lt;class T, class ...Args&gt;
std::array&lt;std::reference_wrapper&lt;T&gt;, sizeof...(Args)&gt; AsIterable(Args&amp; ...args)
{
return {args...};
}
int main()
{
Derived1 var1, var2;
Derived2 var3, var4;
std::cout &lt;&lt; &quot;generic lambda:\n&quot;;
ForEach([](auto&amp; x)
{
printType(x);
}, var1, var2, var3, var4);
std::cout &lt;&lt; &quot;lambda with typed parameter:\n&quot;;
ForEach([](Base&amp; x)
{
printType(x);
}, var1, var2, var3, var4);
std::cout &lt;&lt; &quot;range-based for loop with reference wrapper array:\n&quot;;
for (auto&amp; var : AsIterable&lt;Base&gt;(var1, var2, var3, var4))
{
// note: var has type std::reference_wrapper&lt;Base&gt;&amp;, not Base&amp; as mentioned above
printType(var);
}
std::cout &lt;&lt; &quot;pointer values(main):\n&quot;
&lt;&lt; &amp;var1 &lt;&lt; &#39;\n&#39;
&lt;&lt; &amp;var2 &lt;&lt; &#39;\n&#39;
&lt;&lt; &amp;var3 &lt;&lt; &#39;\n&#39;
&lt;&lt; &amp;var4 &lt;&lt; &#39;\n&#39;
;
std::cout &lt;&lt; &quot;pointer values(lambda):\n&quot;;
ForEach([](Base&amp; x)
{
std::cout &lt;&lt; &amp;x &lt;&lt; &#39;\n&#39;;
}, var1, var2, var3, var4);
std::cout &lt;&lt; &quot;pointer values(range-based for):\n&quot;;
for (auto&amp; var : AsIterable&lt;Base&gt;(var1, var2, var3, var4))
{
std::cout &lt;&lt; &amp;var.get() &lt;&lt; &#39;\n&#39;;
}
}

答案3

得分: 0

如果您更喜欢使用引用而不是指针(正如您在问题中明确指定的),您可以使用 std::reference_wrapper

在下面的示例中,我还使用了 std::array 作为容器,而不是原始的 C 数组:

#include <functional>
#include <array>

class Base { };
class Derived1 : public Base { };
class Derived2 : public Base { };

int main()
{
    Derived1 var1, var2;
    Derived2 var3, var4;

    std::array<std::reference_wrapper<Base>, 4> var_refs = { var1, var2, var3, var4 };
    for (auto & var_ref : var_refs)
    {
        Base& var = var_ref.get();
        // ...
    }
}
英文:

If you prefer to use references over pointers (as you specified explicitly in your question),
you can use std::reference_wrapper.

In the example below I also used std::array instead of a raw C array as a container:

#include &lt;functional&gt;
#include &lt;array&gt;
class Base { };
class Derived1 : public Base { };
class Derived2 : public Base { };
int main()
{
Derived1 var1, var2;
Derived2 var3, var4;
std::array&lt;std::reference_wrapper&lt;Base&gt;, 4&gt; var_refs = { var1, var2, var3, var4 };
for (auto &amp; var_ref : var_refs)
{
Base&amp; var = var_ref.get();
// ...
}
}

huangapple
  • 本文由 发表于 2023年8月10日 21:07:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76876024.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定