Şablon sınıflarının oluşturulması için for döngüsünde const değişkeni nasıl olur?


15

Gibi bir kodum var

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

Şimdi sınıf örnekleri oluşturmak ve gibi bir çok değer kümesi için bir for döngüsü içindeki işlevleri çağırmak istiyorum

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

Bu nasıl yapılır? Bunu yapmak için bir yöntem umuyorum.


Şablon parametresi olarak kullanılması Ngereken constexprbir döngü değişkeni olması durumunda olması gerekir
CoryKramer

Yapamazsınız, A gerçekten bir şablon olmalı mı?
Alan Birtles

Evet, A sınıfının bazı nedenlerden dolayı şablon olması için bir ihtiyaç var ve bu bir şey modeli, bu yüzden bir şablon sınıfı
olmalı

Yanıtlar:


11

Bu template for, beklenen form genişleme ifadelerinin alacağı bir şey gerektirir ; bu, for döngüsü gibi görünen bir şeydir, ancak gerçekte birden çok kez örneklenen bir işlevde şablonlanmış bir bloktur.

Tabii ki, bir geçici çözüm var. Bir tür yerel şablonlu blok ilan etmek ve kendimizi örneklemek için genel lambdaları kötüye kullanabiliriz:

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

Bu işlev bir tamsayı dizisi alır ve lambdayı dizinin Fuzunluğu kadar çok başlatır .

Bu şekilde kullanılır:

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

Burada, Ntamsayı türüne bir constexpr dönüştürme operatörü olan bir nesne olduğu için template parametresi olarak gönderilebilir. Daha doğrusu,std::integral_constant artan bir değere sahip bir.

Canlı örnek


3
Ugh. Böyle eğlenceli bir şablon gördüğümde, daha sonra bir calltack olmadan daha sonra hata ayıklamak zorunda kalacağımı biliyorum ve neler olduğunu tahmin etmek zorundayım ... :)
Michael Dorgan

Amaç nedir static_cast<void>?
Ayxan

2
@Ayxan, lambda fvirgül operatörünü aşırı yükleyen bir tür döndürdüğünde sorunlardan kaçınır
Guillaume Racicot

@MichaelDorgan Bu yüzden ihtiyacımız var template for. Bunun gibi dil yapılarını kötüye kullanmak her zaman daha acı vericidir
Guillaume Racicot

@GuillaumeRacicot veya meta programlama için şablonlardan daha iyi soyutlamalara ihtiyacımız var.
Ajay Brahmakshatriya

5

Nİhtiyaçları normal ile derleme zamanı sabiti olmak fordöngü mümkün değildir.

Ancak, birçok geçici çözüm vardır. Örneğin, bu SO gönderisinden esinlenerek , aşağıdakine benzer bir şey yapabilirsiniz. ( Canlı bir demoya bakın )

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

Şuraya yazdırır 1:100


İçinde , yukarıda tek bir şablon indirgenebilir AGeneratorkullanılarak (yani, uzmanlık önlenebilir) sınıfı if constexpr. ( Canlı bir demoya bakın )

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

Çıktı :

1
2
3
4
5
6
7
8
9
10

Yineleme aralığını sağlamak için aşağıdakileri kullanabilirsiniz. ( Canlı bir demoya bakın )

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

Yukarıdaki sürümle aynı çıktıları verir.


4

C ++ 20'den şablon lambdas'ı kullanabilirsiniz, böylece aşağıdaki gibi bir şey deneyebilirsiniz

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

Aşağıdaki, 0 ile 99 arasındaki tüm sayıları yazdıran tam bir derleme örneğidir

#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

Bunu yapmanın bir yolu, böyle bir şeyle şablon meta programlamasıdır:

#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

Sadece tamlık için - Eğer fonksiyonun tek kullanımı döngüden çağrılmak isteniyorsa, sınıf veya fonksiyonun ayarlanması gerçekten gerekli midir?

Eğer öyleyse ve elle yazmak istemiyorsanız boost.hana'ya bakın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.