Quá tải một chức năng bằng cách sử dụng các mẫu


34

Tôi đang cố gắng xác định một hàm bằng các mẫu và tôi muốn tên kiểu là int hoặc anEnum (một enum cụ thể mà tôi đã xác định). Tôi đã thử những điều sau đây nhưng tôi đã thất bại:

template <int | anEnum T> // or <int T, anEnum T> or <int, anEnum T>
bool isFunction(const T &aVariable){}

Những gì tôi đang cố gắng làm là sử dụng các mẫu, thay vì xác định hai hàm quá tải. Tôi muốn hàm được gọi như sau, mà không cần lập trình viên phải xem xét kiểu

isFunction(aVariable) // and not isFunction<int> (aVariable) nor isFunction<anEnum> (aVariable)

Về cơ bản, tôi muốn hàm này được tạo khuôn mẫu cho các kiểu int và aNum. Tôi đã tìm kiếm điều này, nhưng không thể tìm thấy câu trả lời. Tôi có thể thiếu gì? Cảm ơn bạn,


Nếu nó chính xác là một enum hoặc kiểu int, tại sao không chỉ đơn giản là viết cả hai hàm? Tại sao bạn cần một mẫu trong trường hợp đó?
Klaus

Còn các loại khác thì sao? Bạn có muốn trả về falsecho các loại khác hoặc muốn không khởi tạo chức năng cho các loại khác.
ếchatto

@frogatto Không, giá trị trả về bool không có gì với các loại.
bg

@Klaus Tôi đã yêu cầu tìm hiểu các lựa chọn thay thế. Dựa trên các câu trả lời hiện tại, tôi đã quyết định chỉ cần xác định cả hai chức năng.
bg

Câu trả lời:


25

Ngoài câu trả lời không phải C ++ 20, nếu bạn có thể sử dụng C ++ 20conceptstính năng của nó , tôi sẽ đề nghị bạn thực hiện như sau:

#include <iostream>
#include <concepts>

enum class MyEnum {
    A,
    B,
    C
};

template <typename T>
concept IntegralOrEnum = std::same_as<MyEnum, T> || std::integral<T>;

template <IntegralOrEnum T>
bool isFunction(T const& aVariable) {
    return true;
}

int main() {
    isFunction(MyEnum::A);
    isFunction(3);
    isFunction("my_string"); // error
    return 0;
}

Bản giới thiệu

CẬP NHẬT

Dựa theo nhận xét của @RichardSmith , đây là một cách tiếp cận có thể mở rộng và tái sử dụng nhiều hơn:

template <typename T, typename ...U>
concept one_of = (std::is_same_v<T, U> || ...);

template <one_of<int, MyEnum> T>
bool isFunction(T const& aVariable) {
    return true;
}

Đối với trường hợp cụ thể yêu cầu loại phải là một trong hai loại cụ thể, một cái gì đó như thế này có thể hoạt động tốt hơn:template<typename T, typename ...U> concept one_of = (std::is_same_v<T, U> || ...); template<one_of<int, MyEnum> T> bool isFunction(T const& aVariable) {
Richard Smith

1
@RichardSmith Tôi cũng đã cập nhật câu trả lời của mình. Tôi thấy điều này có thể tái sử dụng và mở rộng hơn. Cảm ơn
NutCracker

21

Có một vài cách để thực hiện điều này. Tất cả liên quan đến việc sử dụngtype_traits tiêu đề. Ví dụ, bạn có thể xác nhận tĩnh các loại trong câu hỏi trong phần thân của hàm.

Hoặc, nếu bạn cần xem xét chức năng này trong số các tình trạng quá tải khác, kỹ thuật SFINAE có thể được sử dụng.

template<typename T>
auto isFunction(const T &aVariable) 
  -> std::enable_if_t<std::is_same<T, int>::value || std::is_same<T,anEnum>::value, bool> {
}

Điều này sẽ loại bỏ chức năng khỏi một bộ quá tải trước khi nó được gọi nếu các loại không khớp. Nhưng nếu bạn không cần hành vi này, một xác nhận tĩnh sẽ cho phép thông báo lỗi thân thiện với lập trình viên hơn.


3

Giải pháp này thì sao? Một mã với chức năng sẽ được biên dịch nếu loại T thỏa mãn các yêu cầu của bạn. Nếu không, xác nhận tĩnh thất bại.

#include <type_traits>
enum anEnum {
    //
};

template <typename T, bool defined = std::is_same<T, int>::value ||
                                     std::is_same<T, anEnum>::value>
bool isFunction(const T& aVariable)
{
    static_assert(defined, "Invalid specialization");

    bool result = false;
    // Put your code here
    return result;
}

1
Điều này không hoạt động tốt với độ phân giải quá tải nếu có chữ ký khác (ví dụ, giả thuyết isFunction(std::string_view)). Chữ ký vẫn sẽ là một kết quả hợp lệ, nhưng khởi tạo gây ra lỗi.
LF

Bạn có thể khai báo chữ ký vô dụng là đã xóa: bool isFunction (std :: string_view) = xóa;
ixjxk

Tôi đang nói về quá tải bổ sung. Trong trường hợp đó, chữ ký không hợp lệ này có thể là một kết hợp chính xác (ví dụ: đối với chuỗi ký tự), do đó ngăn chặn quá tải.
LF

0

Tôi đã cải thiện https://stackoverflow.com/a/60271100/12894563 câu trả lời. 'Nếu constexpr' có thể giúp đỡ trong tình huống này:

template <typename T>
struct always_false : std::false_type {};

template <typename T>
bool isFunction(const T& aVariable)
{
    if constexpr(std::is_same_v<T, int> || std::is_same_v<T, anEnum>)
    {
        std::cout << "int\n";
        // put your code here
        return true;
    }
    else
    {
        static_assert(always_false<T>::value, "You should declare non-template function or write if constexpr branch for your type");
        return false;
    }
}

bool isFunction(std::string_view)
{
    std::cout << "std::string_view\n";
    return true;
}

int main()
{
    isFunction(std::string_view("1L"));
    isFunction(1);
    //isFunction(1L); // will produce an error message from static_assert
}

isFunction (1L) sẽ thất bại vì không có chức năng bị quá tải hoặc nhánh 'if constexpr'.

CẬP NHẬT: Đã sửa lỗi

template <typename T>
struct always_false : std::false_type {};

https://godbolt.org/z/eh4pVn


static_assert(false, ...)là NDR không định hình, thậm chí không được sử dụng. Nếu bạn may mắn, trình biên dịch của bạn sẽ cho bạn biết ngay lập tức, giống như Clang, godbolt.org/z/m_Gk9n
StoryTeller - Unslander Monica

Cảm ơn rất nhiều cho nhận xét của bạn, tôi đã làm sai. Đã sửa lỗi
ixjxk
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.