Melewati konsep ke fungsi

12

Karena konsep didefinisikan sebagai predikat waktu kompilasi, apakah mungkin untuk menggunakan kembali predikat ini untuk algoritme waktu kompilasi? Misalnya, apakah mungkin untuk memeriksa apakah semua tipe dalam tuple sesuai dengan konsep? Sejauh yang saya lihat, tidak mungkin untuk meneruskan konsep ke fungsi dengan cara apa pun, jenis apa yang membuat saya kembali menggunakan templat untuk kasus-kasus ini.

#include <type_traits>

template<typename T>
concept FloatLike = std::is_same_v<T, float>;

struct IsFloat
{
    template<typename U>
    constexpr static bool test()
    {
       return FloatLike<U>;
    }
};


template<typename Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate::template test<T>() && ...);
}


int main()
{
   static_assert(all_types<IsFloat, float, float>());
   static_assert(!all_types<IsFloat, float, int>());
}

Apa yang ingin saya lakukan adalah sesuatu seperti ini, jadi saya tidak harus membungkus konsep itu sepanjang waktu untuk dapat menggunakannya:

template<concept Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate<T> && ...);
}


int main()
{
   static_assert(all_types<FloatLike, float, float>());
   static_assert(!all_types<FloatLike, float, int>());
}

Apakah ada cara untuk lebih dekat dengan ini?

Andreas Loanjoe
sumber
Dan kemudian akan ada proposal untuk menambahkan konsep konsep ... BTW, all_types()dapat secara signifikan disederhanakan menggunakan ekspresi lipat ... &&:return (... && Predicate::template test<Ts>());
Evg
@Evg akan lebih baik :)
Igor R.

Jawaban:

5

Apakah ada cara untuk lebih dekat dengan ini?

Yah, tidak, tidak juga. Tidak di C ++ 20. Tidak ada gagasan dalam bahasa parameter template konsep hari ini. Bahkan templat variabel tidak dapat digunakan sebagai parameter templat. Jadi, jika memiliki konsep untuk memulai, kita tidak dapat menghindari pembungkus.

Tapi yang bisa kita lakukan adalah menulis pembungkus yang lebih sederhana. Jika kita setuju untuk menggunakan ciri tipe "gaya lama" sebagai predikat, khususnya yang berperilaku seperti std::integral_constants, maka kita dapat memiliki definisi "konsep" yang cukup singkat yang dapat digunakan sebagai predikat.

template<typename T>
using FloatLike = std::is_same<T, float>;

template<template <typename> class Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate<T>{} && ...);
}

Sejauh yang bisa saya dapatkan , itu bagus .

StoryTeller - Unslander Monica
sumber
Apakah ini akan berhasil dengan menyatakan lambda generik sebagai templat templat dengan cara apa pun? Sepertinya lambda tidak pernah menjadi template meskipun benar, hanya operator panggilan?
Andreas Loanjoe
@AndreasLoanjoe - Memang. Lambda tidak pernah menjadi templat. Tetapi jika Anda bersedia untuk menyebarkan lambda, maka C ++ 20 memungkinkan Anda untuk melakukannya. Saya dapat menambahkan varian itu dalam beberapa menit.
StoryTeller - Unslander Monica
@AndreasLoanjoe - Dipikir-pikir, lambda masih keluar sangat verbose. Saya tidak berpikir itu alternatif yang bagus. Ini dia pula godbolt.org/z/QSHy8X
StoryTeller - Unslander Monica
Saya berharap mereka akan menambahkan sesuatu yang lebih baik :), tapi ya sepertinya ini adalah jawabannya, hanya ciri-ciri tipe gaya yang menawarkan konsep fungsi ini tidak (belum).
Andreas Loanjoe
0

Jika tujuan Anda adalah "memeriksa apakah semua jenis dalam tupel sesuai dengan konsep" , maka Anda dapat melakukan sesuatu seperti ini:

// concept to check if all types in Ts are the same as T
template<typename T, typename... Ts>
concept AllSame = (std::is_same_v<T,Ts> && ...);

// function only accepts floats as template parameters
template<AllSame<float>... Floats>
constexpr void float_foo()
{
}

// function only accepts ints as template parameters
template<AllSame<int>... Ints>
constexpr void int_foo()
{
}

// function only accepts T as template parameters
template<typename T, AllSame<T>... Ts>
constexpr void foo()
{
}

int main()
{
    int_foo<int, int, int>();
    // int_foo<int, int, double>(); // fails to compile
    float_foo<float, float, float>();
    // float_foo<float, float, int>(); // fails to compile
    foo<int, int, int, int>();
    // foo<int, int, int, float>(); // fails to compile
    foo<double, double, double, double>();
    // foo<double, double, double, int>(); // fails to compile

}

LIVE DEMO

kanstar
sumber
Mengapa AllSamevariadic Anda ? Setiap parameter template dalam paket yang diperkenalkan oleh tipe-kendala secara terpisah sudah dibatasi.
Davis Herring
@ Davidvis saya tidak mengerti. Apakah maksud Anda konsep itu sendiri atau parameter template *_foo()?
kanstar
Aku berarti bahwa kode Anda memiliki karya jika Anda menghapus ...pada Tsdan && ...yang menggunakan itu. (Jelas nama AllSameitu kemudian tidak pantas, tetapi saya tidak yakin mengapa saya ingin menyatakan penghitungan dengan unary <int,int,int>).
Davis Herring
@ Davidviser Kemudian konsep tidak akan AllSametetapi SameAs(lihat en.cppreference.com/w/cpp/concepts/same_as ) dan OP ingin memiliki konsep yang mengambil sejumlah variabel templat parameter.
kanstar
Jelas itu akan terjadi std::same_as. Saya tidak berpikir bagian variadik adalah intinya: itu adalah variabel identitas (yang diinginkan) dari konsep tersebut. Dan poin saya adalah bahwa aspek variadik dari contoh konsep Anda tidak relevan dengan penggunaannya (karena konsep non-variadik sudah berfungsi dengan paket parameter templat).
Davis Herring