จะมีตัวแปร const ใน for for loop สำหรับการสร้าง class template ได้อย่างไร?


15

ฉันมีรหัสเช่น

template <size_t N>
class A
{
    template <size_t N>
    someFunctions() {};
};

ตอนนี้ฉันต้องการสร้างอินสแตนซ์ของคลาสและเรียกใช้ฟังก์ชันในการวนรอบสำหรับชุดของค่าจำนวนมากเช่น

// in main()

int main()
{
    for (int i = 1; i <= 100; i++)
    {
        const int N = i;  // dont know how to do this
        A<N> a;
        a.functionCalls();
    }
}

ทำอย่างไร หวังว่าจะมีวิธีการทำเช่นนี้


เพื่อที่จะใช้เป็นพารามิเตอร์เทมเพลตNจำเป็นต้องใช้constexprซึ่งหากเป็นตัวแปรลูปที่ไม่ใช่ตัวพิมพ์ใหญ่
CoryKramer

คุณทำไม่ได้ A จำเป็นต้องเป็นเทมเพลตจริงๆหรือไม่
Alan Birtles

ใช่จำเป็นต้องให้คลาส A เป็นเทมเพลตด้วยเหตุผลบางอย่างและเป็นโมเดลของบางสิ่งบางอย่างดังนั้นจึงจำเป็นต้องเป็นคลาสเทมเพลต
nachiappan venkatesh

คำตอบ:


11

สิ่งนี้จะต้องมีสิ่งที่เรียกว่า a template forซึ่งเป็นคำสั่งการขยายแบบฟอร์มที่คาดว่าจะใช้ซึ่งเป็นสิ่งที่มีลักษณะคล้ายกับ for loop แต่ในความเป็นจริงคือบล็อก templated ในฟังก์ชันที่มีการเปิดใช้งานหลายครั้ง

แน่นอนว่ามีวิธีแก้ปัญหา เราสามารถละเมิด lambdas ทั่วไปเพื่อประกาศบล็อกเทมเพลตท้องถิ่นบางอย่างและติดตั้งมันเอง:

template <typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F f) {
    (static_cast<void>(f(std::integral_constant<T, S>{})), ...);
}

ฟังก์ชั่นนี้ใช้ลำดับเลขจำนวนเต็มและยกตัวอย่างแลมบ์ดาFตามจำนวนความยาวของลำดับ

มันถูกใช้แบบนี้:

for_sequence(std::make_index_sequence<100>(), [](auto N) { /* N is from 0 to 99 */
  A<N + 1> a; /* N + 1 is from 1 to 100 */
  a.functionCalls();
});

ที่นี่Nสามารถส่งเป็นพารามิเตอร์เทมเพลตได้เนื่องจากเป็นวัตถุที่มีตัวดำเนินการแปลง constexpr เป็นประเภทจำนวนเต็ม ที่แม่นยำยิ่งกว่าคือมันstd::integral_constantมีมูลค่าเพิ่มขึ้น

ตัวอย่างสด


3
ฮึ. เมื่อฉันเห็นเทมเพลตสนุกแบบนี้ฉันเพิ่งรู้ว่าฉันจะต้องแก้ไขข้อบกพร่องในภายหลังโดยไม่ต้องโทรกลับและต้องเดาว่าเกิดอะไรขึ้น ... :)
Michael Dorgan

มีจุดประสงค์static_cast<void>อะไร?
Ayxan

2
@Ayxan หลีกเลี่ยงปัญหาเมื่อแลมบ์ดาfส่งคืนชนิดที่โอเวอร์โหลดตัวดำเนินการเครื่องหมายจุลภาค
Guillaume Racicot

@MichaelDorgan template forนี่คือเหตุผลที่เราต้อง การใช้ภาษาในทางที่ผิดเช่นนี้มักจะเจ็บปวดมากกว่านี้
Guillaume Racicot

@ GuillaumeRacicot หรือเราต้องการ abstractions ที่ดีกว่า template สำหรับการเขียนโปรแกรม meta
Ajay Brahmakshatriya

5

Nความต้องการให้คงที่รวบรวมเวลาที่อยู่กับปกติforห่วงเป็นไปไม่ได้

แต่มีวิธีแก้ไขหลายวิธี ตัวอย่างเช่นแรงบันดาลใจจากโพสต์ SOนี้คุณสามารถทำสิ่งต่อไปนี้ ( ดูตัวอย่างสด )

template<size_t N>
class A
{
public:
    // make the member function public so that you can call with its instance
    void someFunctions()
    {
        std::cout << N << "\n";
    };
};

template<int N> struct AGenerator
{
    static void generate()
    {
        AGenerator<N - 1>::generate();
        A<N> a;
        a.someFunctions();
    }
};

template<> struct AGenerator<1>
{
    static void generate()
    {
        A<1> a;
        a.someFunctions();
    }
};

int main()
{
    // call the static member for constructing 100 A objects
    AGenerator<100>::generate();
}

พิมพ์1ไปที่100


ในดังกล่าวข้างต้นสามารถลดลงได้แม่แบบเดี่ยวAGeneratorชั้น (เช่นความเชี่ยวชาญสามารถหลีกเลี่ยงได้) if constexprโดยใช้ ( ดูตัวอย่างสด )

template<std::size_t N>
struct AGenerator final
{
    static constexpr void generate() noexcept
    {
        if constexpr (N == 1)
        {
            A<N> a;
            a.someFunctions();
            // .. do something more with `a`
        }
        else
        {
            AGenerator<N - 1>::generate();
            A<N> a;
            a.someFunctions();
            // .. do something more with `a`
        }
    }
};

ผลผลิต :

1
2
3
4
5
6
7
8
9
10

ในกรณีที่มีช่วงของการทำซ้ำคุณสามารถใช้ต่อไปนี้ ( ดูตัวอย่างสด )

template<std::size_t MAX, std::size_t MIN = 1> // `MIN` is set to 1 by default
struct AGenerator final
{
    static constexpr void generate() noexcept
    {
        if constexpr (MIN == 1)
        {
            A<MIN> a;
            a.someFunctions();
            // .. do something more with `a`
            AGenerator<MAX, MIN + 1>::generate();
        }
        else if constexpr (MIN != 1 && MIN <= MAX)
        {
            A<MIN> a;
            a.someFunctions();
            // .. do something more with `a`
            AGenerator<MAX, MIN + 1>::generate();
        }
    }
};

int main()
{
    // provide the `MAX` count of looping. `MIN` is set to 1 by default
    AGenerator<10>::generate();
}

ส่งออกเช่นเดียวกับรุ่นข้างต้น


4

จาก C ++ 20 คุณสามารถใช้ lambdas แม่แบบเพื่อให้คุณสามารถลองสิ่งต่อไปนี้

[]<int ... Is>(std::integer_sequence<int, Is...>)
 { (A<Is+1>{}.functionCall(), ...); }
   (std::make_integer_sequence<int, 100>{});

ต่อไปนี้เป็นตัวอย่างการรวบรวมเต็มรูปแบบที่พิมพ์ตัวเลขทั้งหมดจาก 0 ถึง 99

#include <utility>
#include <iostream>

int main()
 {
  []<int ... Is>(std::integer_sequence<int, Is...>)
   { (std::cout << Is << std::endl, ...); }
     (std::make_integer_sequence<int, 100>{});
 }

1

วิธีหนึ่งที่คุณสามารถทำได้คือใช้การเขียนโปรแกรมเมตากับเทมเพลตที่มีลักษณะดังนี้:

#include <iostream>

template <std::size_t N>
struct A {
  void foo() { std::cout << N << '\n'; }
};

template <std::size_t from, std::size_t to>
struct call_foo {
  void operator()() {
    if constexpr (from != to) {
      A<from + 1>{}.foo();
      call_foo<from + 1, to>{}();
    }
  }
};

int main() { call_foo<0, 100>{}(); }

0

เพื่อความสมบูรณ์ - มันจำเป็นสำหรับชั้นเรียนหรือฟังก์ชั่นหรือไม่ถ้าหากการใช้งานฟังก์ชั่นเดียวถูกเรียกจากลูป

ถ้าเป็นเช่นนั้นและคุณไม่ต้องการเขียนด้วยมือดูที่ boost.hana

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.