Làm cách nào tôi có thể ngăn C ++ đoán đối số mẫu thứ hai?


26

Tôi đang sử dụng thư viện C ++ ( strf ), ở đâu đó trong đó, có đoạn mã sau:

namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }

template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}

Bây giờ, tôi muốn sử dụng strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)trong mã của tôi. Nhưng nếu tôi làm như vậy, tôi sẽ gặp lỗi sau (với NVCC của CUDA 10.1):

error: more than one instance of overloaded function "strf::range" matches the argument list:
            function template "auto strf::range(ForwardIt, ForwardIt)"
            function template "auto strf::range(const Range &, const CharT *)"
            argument types are: (util::constexpr_string::const_iterator, util::constexpr_string::const_iterator)

Các thư viện đang có lẽ có thể được thay đổi để tránh điều này (ví dụ sử dụng:

inline auto range(const typename std::enable_if<not std::is_pointer<typename std::remove_cv<Range>::type>::value, Range &>::type range, const CharT* sep)

để đảm bảo Rangekhông phải là một con trỏ); nhưng tôi không thể thay đổi ngay bây giờ. Thay vào đó, tôi muốn bằng cách nào đó chỉ ra cho trình biên dịch rằng tôi thực sự có nghĩa là chỉ có một đối số mẫu, không phải là một đối số được chỉ định và một đối số khác được suy ra.

Tôi có thể làm điều đó?

Sẽ đánh giá cao câu trả lời cho C ++ 11 và C ++ 14; C ++ 17 câu trả lời liên quan đến hướng dẫn khấu trừ ít liên quan hơn nhưng nếu bạn có một câu hỏi, vui lòng đăng nó (cho các phiên bản NVCC trong tương lai ...)


Cập nhật: Bản thân thư viện strf đã được cập nhật để tránh tình trạng này, nhưng câu hỏi vẫn được đặt ra.


1
Tôi đoán việc vượt qua một trình vòng lặp tùy chỉnh chỉ mỏng một char*nhưng không phải là một giải pháp?
Konrad Rudolph

1
@KonradRudolph: Đó là một cách giải quyết, nhưng không trả lời câu hỏi của tôi. Tôi thực sự đã có một cách giải quyết khác (cụ thể là những gì trong /*...*/), nhưng tôi muốn đi đường cao tốc ở đây.
einpoklum

1
Trong trường hợp đó, câu trả lời (đoán) của tôi là không thể thực hiện được, thật không may. Để công bằng, tôi không chắc chắn tôi sẽ chấp nhận cách giải quyết được đề xuất trong mã của riêng tôi.
Konrad Rudolph

Chỉ cần làm rõ: Bạn có muốn một giải pháp chung, sẽ luôn hoạt động để phân biệt cuộc gọi giữa quá tải mẫu với một so với hai tham số hoặc bạn chỉ muốn một giải pháp cụ thể cho trường hợp này?
quả óc chó

@walnut: Giải pháp chung sẽ tốt hơn; kịch bản cụ thể của tôi chủ yếu là động lực cho vấn đề.
einpoklum

Câu trả lời:


16
template<typename T>
inline constexpr auto range1_ptr = strf::range<T>;

template<typename T>
inline decltype(auto) range1(T begin, T end) {
    return range1_ptr<T>(begin, end);
}

Sau đó gọi range1thay strf::range.

range1_ptr<T>(...)luôn luôn có thể được sử dụng để gọi một cách rõ ràng mẫu lấy một đối số mẫu, nhưng không thực hiện bất kỳ suy luận nào từ các đối số. range1nhân rộng các khoản khấu trừ từ strf::rangemẫu ban đầu .

Điều này hoạt động, bởi vì [temp.deduct.funcaddr] / 1 nói rằng việc khấu trừ đối số khuôn mẫu khi lấy địa chỉ của hàm mà không có loại chuyển đổi đích được thực hiện trên mỗi mẫu hàm ứng cử viên như thể danh sách tham số và đối số của một cuộc gọi giả định là trống. Vì vậy, đối số mẫu thứ hai không thể được suy ra cho quá tải thứ hai với hai tham số mẫu. Ứng cử viên duy nhất còn lại là quá tải đầu tiên, sẽ được chọn làm mục tiêu của con trỏ hàm.

Miễn là không có mẫu hàm ứng cử viên thứ hai mà id mẫu hợp lệ chỉ có một đối số có thể được tạo, range1_ptrluôn có thể được sử dụng để gọi mẫu hàm lấy một đối số một cách rõ ràng. Nếu không, việc khởi tạo range1_ptrsẽ đưa ra một lỗi vì sự mơ hồ.


Sẽ không có sự mơ hồ về strf::range<T>?
einpoklum

1
@einpoklum Nó biên dịch tốt trên GCC và Clang. Tôi đã không kiểm tra tiêu chuẩn, nhưng sẽ ngạc nhiên nếu điều đó được cho là mơ hồ.
quả óc chó

Có lẽ bạn nên thay đổi tên chức năng thành pretty_please_with_sugar_on_top()? ... C ++ đôi khi có thể rất kỳ lạ ...
einpoklum

Bạn đã quản lý để hủy bỏ câu trả lời được chấp nhận :-P
einpoklum

11

Còn việc đi qua a usingthì sao?

using tfp = void(*)(char const *, char const *);

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);

Và điều này biên dịch? Dòng thứ hai trông đặc biệt đáng ngờ.
einpoklum

@einpoklum - buồn cười phải không?
max66

@einpoklum - thật không may không phải là một giải pháp chung; hoạt động trong trường hợp này bởi vì (nếu tôi không sai) chỉ có range()phiên bản đầu tiên tương thích với tpf; trường hợp khác có thể khác nhau.
max66

@einpoklum - trong dòng thứ hai, bạn cũng có thể khám phá tham số mẫu ( tfp x = &strf::range<char const *>;); bằng cách này, tôi cho rằng bạn có một giải pháp chung, gần như tương đương với giải pháp của quả óc chó
max66

0

Một giải pháp là

1) trước hết, bạn nên chỉ định loại cho đối số thứ hai, ví dụ: (char *)(some_char_ptr + some_length)

2) không sử dụng constcho cả hai, điều này hoạt động tốt:

strf::range((char *)some_char_ptr, (char *)(some_char_ptr + some_length));

Bạn có thể thử thay thế (char *)bằng (const char *)bên trái HOẶC bên phải, nó vẫn hoạt động.


Điều này là khá xấu xí nếu các đối số chỉ vào constdữ liệu.
aschepler

1. Không đối số thứ hai. Tôi muốn mẫu đối số duy nhất. 2. Hack thú vị! -1 cho đề xuất đầu tiên và +1 cho lần thứ hai :-P
einpoklum
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.