Làm thế nào để trả về đúng loại dữ liệu trong các mẫu?


9
#include <iostream>
using namespace std;

template <class X, class Y>
Y big(X a, Y b)
{
   if (a > b)
      return (a);
   else return (b);
}

int main()
{
   cout << big(32.8, 9);
}

Ở đây tôi đang sử dụng các mẫu trong CPP, vì vậy khi tôi gọi hàm bigbỏ qua các đối số doubleintkiểu, tôi muốn câu trả lời là double. Các loại ở đây, nó trả về 32thay vì 32.8.

Làm thế nào tôi có được đầu ra mong muốn của tôi? Làm thế nào để viết một kiểu trả về đúng của bighàm?


1
Một hàm chỉ có thể trả về một loại cố định. Bạn không thể chọn thời gian chạy để trả về loại nào.
Jesper Juhl

1
Bạn có thể muốn xem làm thế nào std::maxđược thực hiện. Kiểu trả về của một hàm phải được biết tại thời điểm biên dịch trong C ++. Vì vậy, bạn không thể có kiểu trả về này phụ thuộc vào giá trị thời gian chạy của các tham số. Đây là lý do tại sao đối với chức năng như vậy, bạn cần cả hai tham số để có cùng loại (nghĩa là có loại X, nhưng không phải là Y).
Boris Dalstein

Câu trả lời:


12

Một hàm chỉ có thể có một kiểu trả về phải được biết tại thời điểm biên dịch. Tuy nhiên, bạn có thể sử dụng std::common_type, để trả về một loại mà cả hai tham số có thể được chuyển đổi thành ẩn.

Đó sẽ là

#include <type_traits>
template <class X, class Y>
typename std::common_type<X,Y>::type big(X a, Y b)
{
   if (a > b)
      return a;
   else return b;
}

Và để kiểm tra xem nó có thực sự trả về doublekhi được thông qua intdoublechúng ta có thể làm gì:

int main() {
    auto x = big(4.2,42);
    std::cout << std::is_same<decltype(x),double>::value;
}

Mà in

1

PS: std::common_typecó thể sử dụng toán tử ternary đằng sau các mùi và như vậy giải pháp này không khác nhiều so với các câu trả lời khác ( auto+ ternary). Sức mạnh thực sự của std::common_typenó là nó chấp nhận bất kỳ số lượng tham số.


10

Kiểu trả về phải được xác định tại thời điểm biên dịch. Bạn có thể sử dụng quay trở lại với toán tử có điều kiện , nếu bạn bị giới hạn ở .

template <typename X, typename Y>
auto big(X&& a, Y&& b) -> decltype(a > b ? a : b) // ---> like this
{
   return  a > b ? a : b;
}

Xem trực tiếp


Tuy nhiên, nếu bạn có quyền truy cập vào hoặc autotrả về cao hơn là đủ, vì trình biên dịch sẽ suy ra đúng loại nếu bạn sử dụng nó cùng với toán tử điều kiện như sau:

template <typename X, typename Y>
auto big(X a, Y b)
{
   return  a > b ? a : b;
}

Xem trực tiếp


Loại trả về Trailing không cần thiết, ít nhất là từ C ++ 14.
sweenish

@walnut Điểm tốt. Một lựa chọn nữa là tham chiếu chuyển tiếp?
JeJo

1
@JeJo Vâng, tôi cho rằng điều đó cũng tốt, nhưng có lẽ là vô nghĩa, bởi vì bạn không sửa đổi một trong hai đối số và kiểu trả về vẫn sẽ là một tham chiếu giá trị trong cả hai trường hợp (mặc dù có khả năng là không const).
quả óc chó

Tôi đã xóa nhận xét của mình vì chúng không áp dụng nữa, nhưng tôi sẽ đề nghị thêm cảnh báo vào câu trả lời rằng bạn không thể lấy tham số theo giá trị.
quả óc chó

Nếu ai đó nhìn vào mã của bạn, có vẻ như tham số đã truyền sẽ quyết định loại trả về mà ai đó sẽ nhận được không phải là trường hợp! Bạn sẽ luôn nhận được gấp đôi, ngay cả khi a lớn hơn b.
Klaus

4

Khi đánh dấu loại trả về của bạn là Yvà chuyển một inttham số thứ hai của bạn, bạn đã chỉ rõ rằng đó Ylà một int. Không có bất ngờ xảy ra ở đây.

#include <iostream>

template <typename X, typename Y>
decltype(auto) big(const X& a, const Y& b)  // return type can just be auto as well 
{
    return a > b ? a : b;
}

int main()
{
    std::cout << big(32.8, 9) << '\n';
    std::cout << big(9, 32.8) << '\n';
    std::cout << big(32.8, 90) << '\n';
    std::cout << big(90, 32.8) << '\n';
}

Điều này in tất cả bốn giá trị chính xác lên màn hình.

https://godbolt.org/z/fyGsmo

Một điều quan trọng cần lưu ý là điều này sẽ chỉ hoạt động đối với các loại có thể so sánh với nhau, tức là trình biên dịch sẽ ngầm chuyển đổi một loại sang loại khác để so sánh.

QUAN TRỌNG : Các tham số cần được thực hiện bằng cách tham chiếu để tránh hành vi không xác định. Điều này có liên quan đến loại trở lại mà tôi cứng đầu gắn bó. decltype(auto)có thể trả về các tham chiếu đến các loại. Nếu bạn trả lại một cái gì đó cục bộ cho hàm (đếm đối số), bạn sẽ có hành vi không xác định.


@walnut Vô tình trả lại một tài liệu tham khảo khó hơn rất nhiều so với trang web này. Nhưng tốt để biết về hành vi không xác định. Nó không giống như đây sẽ là mã tôi viết; đó là một câu trả lời cho một câu hỏi
sweenish

1
Ah. Tôi đọc bình luận trước đó của bạn là hai điểm khác biệt và không ảnh hưởng và nguyên nhân. Tôi có thể thực hiện chỉnh sửa thích hợp.
sweenish

Tôi đã thêm từ chối trách nhiệm.
17:30

2

Đây không phải là giải pháp chính xác cho tình huống chính xác của bạn, trong tất cả khả năng - các câu trả lời khác có thể sẽ gần hơn với những gì bạn muốn.

Tuy nhiên, nếu bạn thực sự cần phải trả về các loại hoàn toàn khác nhau trong thời gian chạy vì một số lý do, giải pháp chính xác (kể từ ) là sử dụng một std::variant, đó là một loại kết hợp an toàn loại.

#include <variant>

template <typename X, typename Y>
std::variant<X, Y> max(X a, Y b) {
  if (a > b)
    return std::variant<X, Y>(std::in_place_index_t<0>, a);
  else
    return std::variant<X, Y>(std::in_place_index_t<1>, b);
}

Lưu ý rằng sau đó, onus nằm trên người gọi để xử lý giá trị trả về, rất có thể là sử dụng std::visithoặc tương tự.


-2

Nó trả về int vì Y là một int và nó đưa 32.8 cho nó. Khi bạn gọi big 32,82 là float, nhưng 8 là int và kiểu trả về hàm là Y, cũng là int.

Bạn thực sự không thể sửa lỗi này vì bạn cần biết trong thời gian chạy loại nào trả về lớn, vì vậy hãy tạo một và b cùng loại như thế này:

    #include <iostream>
    using namespace std;

    template <typename X>

    X big (X a, X b)
    {
    if (a>b)
    return a;

    else return b;
    }

    int main()
    {
    cout<< big (32.8, 9.0);
    }
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.