Truyền một khái niệm cho một chức năng


12

Vì các khái niệm được định nghĩa là các vị từ thời gian biên dịch, nên thực tế cũng có thể sử dụng lại các vị từ này cho các thuật toán thời gian biên dịch? Ví dụ, liệu có thể kiểm tra xem tất cả các loại trong một tuple có phù hợp với một khái niệm không? Theo như tôi đã thấy, không thể truyền khái niệm cho hàm theo bất kỳ cách nào, điều này dẫn tôi trở lại sử dụng các mẫu cho các trường hợp này.

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

Những gì tôi muốn làm là một cái gì đó như thế này, vì vậy tôi không phải bao bọc khái niệm này mọi lúc để có thể sử dụng nó:

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

Có cách nào để đến gần hơn với điều này?


Và sau đó sẽ có một đề xuất để thêm các khái niệm về khái niệm ... BTW, all_types()có thể được đơn giản hóa đáng kể bằng cách sử dụng các biểu thức gấp ... &&:return (... && Predicate::template test<Ts>());
Evg 15/11/19

@Evg nó sẽ rất tuyệt :)
Igor R.

Câu trả lời:


5

Có cách nào để đến gần hơn với điều này?

Vâng, không, không thực sự. Không có trong C ++ 20. Ngày nay không có khái niệm nào về ngôn ngữ của một tham số khái niệm mẫu. Ngay cả các mẫu biến cũng không thể được sử dụng làm tham số mẫu. Vì vậy, nếu có một khái niệm để bắt đầu, chúng ta không thể tránh khỏi việc gói.

Nhưng những gì chúng ta có thể làm là viết các hàm bao đơn giản hơn. Nếu chúng ta đồng ý sử dụng các đặc điểm loại "kiểu cũ" làm vị ngữ, cụ thể là các đặc điểm giống như std::integral_constants, thì chúng ta có thể có các định nghĩa "khái niệm" khá ngắn gọn có thể được sử dụng làm vị ngữ.

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

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

Nó tốt như nó có thể nhận được , như tôi có thể thấy.


Điều này có hoạt động bằng cách từ chối một lambda chung làm mẫu mẫu theo bất kỳ cách nào không? Có vẻ như lambda không bao giờ là một mẫu mặc dù đúng, chỉ có toán tử cuộc gọi?
Andreas Loanjoe

@AndreasLoanjoe - Thật vậy. Một lambda không bao giờ là một mẫu. Nhưng nếu bạn sẵn sàng vượt qua lambdas, thì C ++ 20 cho phép bạn làm điều đó. Tôi có thể thêm một biến thể của nó trong vài phút.
Người kể chuyện - Unslander Monica

@AndreasLoanjoe - Về ý nghĩ thứ hai, một lambda vẫn xuất hiện rất dài dòng. Tôi không nghĩ đó là một sự thay thế tuyệt vời. Đây là dù sao thì godbolt.org/z/QSHy8X
Người kể chuyện - Unslander Monica

Tôi hy vọng họ sẽ thêm một cái gì đó tốt hơn :), nhưng vâng, có vẻ như đây là câu trả lời, chỉ những đặc điểm kiểu kiểu mới cung cấp khái niệm chức năng này chưa (chưa).
Andreas Loanjoe

0

Nếu mục tiêu của bạn là "kiểm tra xem tất cả các loại trong một tuple có phù hợp với một khái niệm không" , thì bạn có thể làm một cái gì đó như thế này:

// 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

}

BẢN THỬ TRỰC TIẾP


Tại sao là AllSamematrixdic của bạn ? Mỗi tham số mẫu trong một gói được giới thiệu bởi một ràng buộc kiểu đã bị ràng buộc riêng biệt.
Davis Herring

@DavisHerring tôi không hiểu. Bạn có nghĩa là khái niệm chính nó hoặc các tham số mẫu trong *_foo()?
kanstar

Tôi có nghĩa là mã bạn có hoạt động nếu bạn loại bỏ ...bật Ts&& ...sử dụng nó. (Rõ ràng cái tên AllSamesau đó sẽ không phù hợp, nhưng tôi không chắc tại sao tôi muốn thể hiện một số lượng đơn nhất là<int,int,int> .)
Davis Herring

@DavisHerring Sau đó, khái niệm này sẽ không AllSamenhưng SameAs(xem en.cppreference.com/w/cpp/con accept / same_as ) và OP muốn có một khái niệm cần một số lượng tham số mẫu khác nhau.
kanstar

Rõ ràng nó sẽ là std::same_as. Tôi không nghĩ phần biến đổi là điểm chính: đó là biến (mong muốn) danh tính của khái niệm. Và quan điểm của tôi là khía cạnh biến động trong ví dụ khái niệm của bạn không liên quan đến việc sử dụng nó (vì các khái niệm không biến đổi đã hoạt động với các gói tham số mẫu).
Davis Herring
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.