Chỉ thực hiện chức năng bên trong mẫu hàm cho những loại có chức năng được xác định


13

Tôi có một mẫu hàm có nhiều loại khác nhau làm đầu vào. Trong số các loại đó chỉ có một trong số chúng có getInt()chức năng. Do đó tôi muốn mã chỉ chạy chức năng cho loại đó. Xin đề xuất một giải pháp. Cảm ơn

#include <type_traits>
#include <typeinfo>

class X {
    public:
    int getInt(){
        return 9;
    }
};

class Y{

};

template<typename T>
void f(T& v){
    // error: 'class Y' has no member named 'getInt'
    // also tried std::is_same<T, X>::value 
    if(typeid(T).name() == typeid(X).name()){
        int i = v.getInt();// I want this to be called for X only
    }
}

int main(){
    Y y;
    f(y);
}

Không liên quan đến vấn đề của bạn, nhưng type_infocấu trúc có một toán tử so sánh bằng , do đó, typeid(T) == typeid(X)cũng nên làm việc.
Một số lập trình viên anh chàng

5
Sử dụng: if constexprvới điều kiện is_same_v<T,X>.
rafix07

Giải pháp cho vấn đề này sẽ chính thức trở nên thanh lịch hơn vào cuối năm nay với các khái niệm. Không siêu hữu ích ngay bây giờ, tôi biết.
sweenish

Có nhiều cách để giải quyết vấn đề của bạn. Một cặp vợ chồng được đề cập ở trên. Bạn cũng có thể sử dụng các đặc điểm của các biến thể khác nhau để xem một loại có getIntthành viên có thể gọi được không . Phải có khá nhiều câu hỏi ở đây trên stackoverflow.com về cách xem cấu trúc hoặc lớp có chức năng thành viên cụ thể không, nếu bạn chỉ tìm kiếm một chút.
Một số lập trình viên anh chàng

Câu trả lời:


10

Nếu bạn muốn có thể gọi một hàm fcho tất cả các loại có thành viên hàm getInt, không chỉ X, bạn có thể khai báo 2 quá tải cho hàm f:

  1. cho các loại có getIntchức năng thành viên, bao gồm cả lớpX

  2. cho tất cả các loại khác, bao gồm cả lớp Y.

Giải pháp C ++ 11 / C ++ 17

Có suy nghĩ đó, bạn có thể làm một cái gì đó như thế này:

#include <iostream>
#include <type_traits>

template <typename, typename = void>
struct has_getInt : std::false_type {};

template <typename T>
struct has_getInt<T, std::void_t<decltype(((T*)nullptr)->getInt())>> : std::is_convertible<decltype(((T*)nullptr)->getInt()), int>
{};

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T,
          typename std::enable_if<!has_getInt<T>::value, T>::type* = nullptr>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <typename T,
          typename std::enable_if<has_getInt<T>::value, T>::type* = nullptr>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

Kiểm tra nó trực tiếp .

Xin lưu ý rằng std::void_tđược giới thiệu trong C ++ 17, nhưng nếu bạn bị giới hạn ở C ++ 11, thì thực sự rất dễ thực hiện void_t:

template <typename...>
using void_t = void;

Và đây là phiên bản C ++ 11 trực tiếp .

Chúng ta có gì trong C ++ 20?

C ++ 20 mang lại rất nhiều điều tốt và một trong số đó là các khái niệm . Trên đây là điều hợp lệ cho C ++ 11 / C ++ 14 / C ++ 17 có thể giảm đáng kể trong C ++ 20:

#include <iostream>
#include <concepts>

template<typename T>
concept HasGetInt = requires (T& v) { { v.getInt() } -> std::convertible_to<int>; };

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <HasGetInt T>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

Kiểm tra nó trực tiếp .


Trước C ++ 17, việc thực hiện đó void_tgây ra sự cố cho một số trình biên dịch cũ (như được chỉ ra bởi liên kết).
Jarod42

Không nhất thiết phải viết hai lần quá tải (thay thế "cần" bằng "có thể" sẽ tốt hơn nhiều)
idclev 463035818

@ idclev463035818 được cập nhật. Cảm ơn
NutCracker

1
@SSAnne được cập nhật
NutCracker

1
Định nghĩa khái niệm là không chính xác. bạn đang gán kết quả cho một int nên khái niệm sẽ làtemplate<typename T> concept HasGetInt = requires (T& v) { {v.getInt()} -> std::convertible_to<int>; };
Hui

8

Bạn có thể sử dụng if constexprtừ C ++ 17:

template<typename T>
void f(T& v){
    if constexpr(std::is_same_v<T, X>) { // Or better create trait has_getInt
        int i = v.getInt();// I want this to be called for X only
    }
    // ...
}

Trước đây, bạn sẽ phải sử dụng quá tải và SFINAE hoặc gửi thẻ.


if constexprlà một tính năng của C ++ 17.
Andrey Semashev

Tuy nhiên, điều này sẽ chỉ hoạt động cho lớp họcX
NutCracker

Câu hỏi hiện được cập nhật thành C ++ 11 / C ++ 14
NutCracker

@NutCracker: Không hay để cập nhật thẻ / câu hỏi và vì vậy làm mất hiệu lực các câu trả lời hiện có ... (ngay cả khi cảnh báo về điều đó là tốt).
Jarod42

tôi vừa cập nhật thẻ ... tiêu đề câu hỏi đã được cập nhật bởi OP
NutCracker

7

Giữ nó đơn giản và quá tải. Đã làm việc kể từ ít nhất C ++ 98 ...

template<typename T>
void f(T& v)
{
    // do whatever
}

void f(X& v)
{
    int result = v.getInt();
}

Điều này là đủ nếu chỉ có một loại có getIntchức năng. Nếu có nhiều hơn, nó không còn đơn giản nữa. Có một số cách để làm điều đó, đây là một cách:

struct PriorityA { };
struct PriorityB : PriorityA { };

template<typename T>
void f_impl(T& t, PriorityA)
{
    // generic version
}

// use expression SFINAE (-> decltype part)
// to enable/disable this overload
template<typename T>
auto f_impl(T& t, PriorityB) -> decltype(t.getInt(), void())
{
    t.getInt();
}

template<typename T>
void f(T& t)
{
    f_impl(t, PriorityB{ } ); // this will select PriorityB overload if it exists in overload set
                              // otherwise PriorityB gets sliced to PriorityA and calls generic version
}

Ví dụ sống với đầu ra chẩn đoán.


1
Trong trường hợp này, nó sẽ hoạt động vì chỉ có một quá tải (cho X), nhưng, nếu có nhiều loại tương tự với thành viên getInttrong tương lai, đây không phải là một thực hành tốt. Bạn có thể muốn lưu ý rằng
NutCracker

@NutCracker Đã làm như vậy.
jrok
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.