Làm thế nào để có một biến const trong một vòng lặp for để tạo các lớp mẫu?


15

Tôi có một mã như

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

Bây giờ tôi muốn tạo các thể hiện của lớp và gọi các hàm trong nó trong một vòng lặp for cho một tập hợp nhiều giá trị như

// 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();
    }
}

làm như thế nào? Hy vọng cho một phương pháp để làm điều này.


Để được sử dụng làm tham số mẫu Ncần phải có constexprnếu đó là biến vòng lặp không phải là trường hợp
CoryKramer

Bạn không thể, A thực sự cần phải là một mẫu?
Alan Birtles

Vâng, cần phải có lớp A để làm mẫu vì một số lý do và nó là mô hình của một cái gì đó vì vậy nó phải là một lớp mẫu
nachiappan venkatesh

Câu trả lời:


11

Điều này sẽ yêu cầu một cái gì đó được gọi template forcâu lệnh mở rộng biểu mẫu dự kiến , đây là thứ trông giống như một vòng lặp for nhưng trong thực tế là một khối templated trong một hàm được truyền tải nhiều lần.

Tất nhiên, có một cách giải quyết. Chúng ta có thể lạm dụng lambdas chung để tuyên bố một số loại khuôn mẫu địa phương và tự kích hoạt nó:

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>{})), ...);
}

Hàm này lấy một chuỗi số nguyên và khởi tạo lambda Fnhiều lần bằng độ dài của chuỗi.

Nó được sử dụng như thế này:

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

Ở đây, Ncó thể được gửi dưới dạng tham số mẫu vì nó là đối tượng có toán tử chuyển đổi constexpr thành kiểu số nguyên. Chính xác hơn, đó là một std::integral_constantgiá trị ngày càng tăng.

Ví dụ sống


3
Ừ Khi tôi thấy mẫu thú vị như thế này, tôi chỉ biết rằng tôi sẽ phải gỡ lỗi nó sau mà không cần một cuộc gọi và phải đoán những gì đang xảy ra ... :)
Michael Dorgan

Mục đích của là static_cast<void>gì?
Ayxan

2
@Ayxan tránh các vấn đề khi lambda ftrả về một loại quá tải toán tử dấu phẩy
Guillaume Racicot

@MichaelDorgan Đây là lý do tại sao chúng ta cần template for. Lạm dụng các cấu trúc ngôn ngữ như thế này luôn luôn đau đớn hơn
Guillaume Racicot

@GuillaumeRacicot hoặc chúng tôi cần trừu tượng hóa tốt hơn các mẫu để lập trình meta.
Ajay Brahmakshatriya

5

Các Nnhu cầu được thời gian biên dịch liên tục, đó là với một bình thường forvòng lặp là không thể.

Nhưng, có nhiều cách giải quyết. Ví dụ, lấy cảm hứng từ bài viết SO này , bạn có thể làm một cái gì đó như sau. ( Xem bản demo trực tiếp )

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

In 1tới100


Trong , phần trên có thể được giảm xuống thành một AGeneratorlớp mẫu duy nhất (nghĩa là có thể tránh chuyên môn hóa) if constexpr. ( Xem bản demo trực tiếp )

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

Đầu ra :

1
2
3
4
5
6
7
8
9
10

Trong trường hợp cung cấp phạm vi lặp, bạn có thể sử dụng như sau. ( Xem bản demo trực tiếp )

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

Đầu ra giống như phiên bản trên.


4

Từ C ++ 20, bạn có thể sử dụng mẫu lambdas, vì vậy bạn có thể thử một cái gì đó như sau

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

Sau đây là một ví dụ biên dịch đầy đủ in tất cả các số từ 0 đến 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

Một cách bạn có thể làm điều này là lập trình meta mẫu với một cái gì đó như thế này:

#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

Chỉ cần hoàn thành - nó thực sự cần thiết cho lớp hoặc hàm được tạo khuôn mẫu, nếu việc sử dụng chức năng duy nhất được gọi từ vòng lặp?

Nếu vậy và bạn không muốn viết bằng tay, hãy xem boost.hana.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.