Làm cách nào tôi có thể ngăn người dùng chỉ định tham số mẫu hàm, buộc phải suy ra?


8

Giả sử tôi có một hàm mẫu:

template <typename A, typename B>
A fancy_cast(B)
{
    return {};
}

Việc sử dụng dự định là một cái gì đó như fancy_cast<int>(1.f).

Nhưng không có gì ngăn người dùng chỉ định tham số mẫu thứ hai theo cách thủ công : fancy_cast<int, int>(1.f), điều này sẽ gây ra sự cố.

Làm thế nào tôi có thể ngăn chặn typename Bđược chỉ định và buộc nó được suy luận?

Tôi đã nghĩ ra điều này:

// Using this wrapper prevents the code from being
// ill-formed NDR due to [temp.res]/8.3
template <auto V> inline constexpr auto constant_value = V;

template <
    typename A,
    typename ...Dummy,
    typename B,
    typename = std::enable_if_t<constant_value<sizeof...(Dummy)> == 0>
>
A fancy_cast(B)
{
    return {};
}

Nó xuất hiện để làm việc, nhưng nó cực kỳ cồng kềnh. Có cách nào tốt hơn?

Câu trả lời:


4

Điều gì về việc tạo fancy_castmột mẫu biến?

template <typename A>
struct fancy_cast_t {
    template <typename B>
    A operator()(B x) const { return x; }
};

template <typename A>
constexpr fancy_cast_t<A> fancy_cast {};

fancy_cast<int>(1.5);  // works
fancy_cast<int, int>(1.5);  // doesn't work
fancy_cast<int>.operator()<int>(1.5);  // works, but no one would do this

3

Đây không phải là giải pháp hiệu quả nhất, nhưng bạn có thể tạo một lớp có tham số mẫu cho loại để chuyển đổi và sau đó có một mẫu hàm tạo có bất kỳ loại nào. Sau đó, nếu bạn thêm một kiểu operator Tcho loại bạn khởi tạo lớp với bạn, bạn có thể trả về giá trị đúng. Điều đó sẽ giống như

template<typename T>
struct fancy_cast
{
    T ret;
    template<typename U>
    fancy_cast(U u) : ret(u) {} // or whatever you want to do to convert U to T
    operator T() && { return std::move(ret); }
};

int main()
{
    double a = 0;
    int b = fancy_cast<int>(a);
}

Điều này hoạt động vì không có cách nào để chỉ định tham số mẫu cho hàm tạo vì bạn thực sự không thể gọi nó.


2

Tôi tìm thấy một giải pháp đẹp trai.

Chúng ta có thể sử dụng gói tham số không loại, loại mà người dùng không thể tạo. 1 Ví dụ: tham chiếu đến một lớp ẩn:

namespace impl
{
    class require_deduction_helper
    {
      protected:
        constexpr require_deduction_helper() {}
    };
}

using require_deduction = impl::require_deduction_helper &;

template <typename A, require_deduction..., typename B>
A fancy_cast(B)
{
    return {};
}

1 Chúng ta phải để lại một kẽ hở để xây dựng một deduction_barrier, nếu không thì mã sẽ là NDR không đúng định dạng . Đó là lý do tại sao các nhà xây dựng được bảo vệ.


1
@ M.Mac Bạn có nghĩa là cho tất cả có thể AB? Tôi không thấy lý do tại sao nó sẽ không.
HolyBlackCat

Tôi không nghĩ rằng bạn cần protected, require_deduction...không cần phải trống rỗng (trái với enable_if_t<sizeof...(Ts) == 0>). Không phải vì bạn không thể xây dựng một số giá trị mà mẫu không hợp lệ. Tương tự, struct S{}; using member_ptr_t = void (S::*)();là hợp lệ, ngay cả khi Skhông có thành viên.
Jarod42

@ Jarod42 Hmm. Khi tôi đọc nó, mệnh đề nói rằng bạn phải có khả năng tạo ra một chuyên môn hợp lệ với một gói không trống, hoặc đó là NDR không định hình. member_ptr_tcó thể được khởi tạo với nullptr, nhưng nếu không thể thì tôi muốn nói rằng bạn không thể tạo ra một gói tham số từ nó.
HolyBlackCat
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.