2013-12-16 17 views
14

Tôi hiện có map<int, std::wstring>, nhưng để linh hoạt, tôi muốn có thể gán một biểu thức lambda, trả về std::wstring làm giá trị trong bản đồ.std :: function như tham số mẫu

Vì vậy, tôi đã tạo biểu mẫu này lớp:

template <typename T> 
class ValueOrFunction 
{ 
private: 
    std::function<T()> m_func; 
public: 
    ValueOrFunction() : m_func(std::function<T()>()) {} 
    ValueOrFunction(std::function<T()> func) : m_func(func) {} 
    T operator()() { return m_func(); } 
    ValueOrFunction& operator= (const T& other) 
    { 
     m_func = [other]() -> T { return other; }; 
     return *this; 
    } 
}; 

và sử dụng nó như:

typedef ValueOrFunction<std::wstring> ConfigurationValue; 
std::map<int, ConfigurationValue> mymap; 

mymap[123] = ConfigurationValue([]() -> std::wstring { return L"test"; }); 
mymap[124] = L"blablabla"; 
std::wcout << mymap[123]().c_str() << std::endl; // outputs "test" 
std::wcout << mymap[124]().c_str() << std::endl; // outputs "blablabla" 

Bây giờ, tôi không muốn sử dụng các nhà xây dựng cho gói các lambda, vì vậy tôi quyết định thêm toán tử gán thứ hai, lần này cho số std::function:

ValueOrFunction& operator= (const std::function<T()>& other) 
{ 
    m_func = other; 
    return *this; 
} 

Đây là poi nt nơi trình biên dịch bắt đầu phàn nàn. Dòng mymap[124] = L"blablabla"; đột nhiên dẫn đến lỗi này:

error C2593: 'operator = is ambiguous'

IntelliSense đưa ra một số thông tin thêm:

more than one operator "=" matches these operands: function "ValueOrFunction::operator=(const std::function &other) [with T=std::wstring]" function "ValueOrFunction::operator=(const T &other) [with T=std::wstring]" operand types are: ConfigurationValue = const wchar_t [10] c:\projects\beta\CppTest\CppTest\CppTest.cpp 37 13 CppTest

Vì vậy, câu hỏi của tôi là, tại sao không phải là trình biên dịch có thể phân biệt giữa std::function<T()>T? Và làm thế nào tôi có thể sửa lỗi này?

Trả lời

12

Vấn đề cơ bản là std::function có hàm tạo ngầm tham lam sẽ cố gắng chuyển đổi mọi thứ và chỉ không thể biên dịch trong nội dung. Vì vậy, nếu bạn muốn quá tải với nó, hoặc không có chuyển đổi để thay thế có thể được cho phép, bạn cần phải vô hiệu hóa các công cụ có thể chuyển đổi để thay thế từ việc gọi quá tải std::function.

Kỹ thuật đơn giản nhất sẽ là gửi thẻ. Thực hiện một operator= đó là tham lam và thiết lập để chuyển tiếp hoàn hảo, sau đó tự cử đến một phương pháp assign với thẻ:

template<typename U> 
void operator=(U&&u){ 
    assign(std::forward<U>(u), std::is_convertible<U, std::wstring>()); 
} 
void assign(std::wstring, std::true_type /*assign_to_string*/); 
void assign(std::function<blah>, std::false_type /*assign_to_non_string*/); 

về cơ bản chúng tôi đang làm thủ nghị quyết tình trạng quá tải.

Nhiều kỹ thuật tiên tiến: (có thể là không cần thiết)

cách tiếp cận khác là để hạn chế std::function= với SFINAE vào luận điểm được gọi là hợp lệ, nhưng đó là Messier.

Nếu bạn có nhiều loại khác nhau cạnh tranh với std::function, bạn phải tự tay gửi tất cả chúng một cách đáng buồn. Cách khắc phục đó là kiểm tra xem loại U của bạn có thể gọi được không và không thể chuyển đổi thành T, sau đó gắn thẻ cho điều đó. Dán không quá std::function quá tải trong nhánh thay thế và để quá tải thông thường xảy ra cho mọi thứ khác.

Có một sự khác biệt nhỏ trong đó một loại có thể chuyển đổi thành std::wstring và chuyển đổi được thành có thể chuyển thành số khác nhau hơn so với giải pháp đơn giản ban đầu ở trên, vì các thử nghiệm được sử dụng không thực sự loại trừ lẫn nhau. Đối với mô phỏng thủ công đầy đủ của quá tải C++ (đã sửa chữa cho sự ngu ngốc của std::function), bạn cần phải thực hiện rằng trường hợp không rõ ràng!

Điều cuối cùng cần làm là sử dụng auto và loại trả về theo sau để cải thiện khả năng mã khác phát hiện nếu = của bạn hợp lệ. Cá nhân, tôi sẽ không làm điều này trước khi C++ 14 trừ dưới duress, trừ khi tôi đã viết một số mã thư viện nghiêm trọng.

+0

Xem nó hoạt động [ở đây] (http://ideone.com/XpOXMF) –

+0

Tôi đã suy nghĩ xem liệu SFINAE có hợp lý hay không và tôi đã chọn không đề xuất nó quá phức tạp. Việc gửi thẻ như được thực hiện ở đây là một giải pháp sạch tốt +1 (mặc dù nó xứng đáng hơn) –

6

Cả hai std::functionstd::wstring đều có toán tử chuyển đổi có thể lấy chuỗi ký tự lớn mà bạn đang chuyển. Trong cả hai trường hợp, chuyển đổi được người dùng xác định và do đó chuỗi chuyển đổi có cùng mức độ ưu tiên, gây ra sự mơ hồ. Đây là nguyên nhân gốc của lỗi.

Các vấn đề liên quan