2016-10-09 13 views
5

Hãy tưởng tượng tôi đã có struct này:Type-chỉ mẫu tranh luận để lambda

struct Foo { 
    operator int() { 
     return 11; 
    } 
    operator unsigned int() { 
     return 22; 
    } 
} foo; 

Khi struct này được đúc để một int, nó sẽ trả 11, nhưng khi đúc đến một int unsigned, nó sẽ trả 22.

sử dụng chức năng bình thường, tôi có thể sử dụng các mẫu và một hàm getter để lựa chọn:

template<typename T> 
T get() { 
    return (T)foo; 
} 

Bây giờ, khi gọi hàm này như get<int>() nó sẽ quay trở lại 11 , nhưng khi gọi nó là get<unsigned int>() nó sẽ trả về 22.

Tất cả mọi thứ của ổn cho đến bây giờ, khi tôi cố gắng sử dụng lambdas thay vì:

auto lambda=[](auto type) { 
    return (decltype(type))foo; 
}; 

Bây giờ khi gọi lambda như lambda(0) nó trả 11, và gọi đó là như lambda(0U) lợi nhuận 22.

Điều này hoạt động chính xác, mặc dù khá 'hacky', nhưng một thể loại của loại cần phải được sử dụng, sẽ không lý tưởng với các loại lớn hơn. Vì vậy, một cách khác sanh ra, thậm chí 'hackier', để đạt được điều này:

auto lambda=[](auto* typePointer) { 
    return (decltype(*typePointer))foo; 
}; 

Bây giờ gọi nó như lambda((int*)NULL) lợi nhuận 11 nhưng gọi nó như lambda((unsigned int*)NULL) lợi nhuận 22. Như bạn có thể đã nhận thấy điều này là khá dài dòng và 'hacky', vì vậy tôi đã thử một phương pháp đơn giản hơn và truyền thống:

auto lambda=[]<typename T>() { 
    return (T)foo; 
}; 

Lúc đầu, tôi nghĩ rằng nó sẽ không biên dịch, vì tôi chưa thấy cú pháp này bất cứ đâu, nhưng nó biên dịch (ít nhất là với GCC). Tuy nhiên, khi cố gắng gọi nó, lỗi xuất hiện:

lambda(); 


testlambda.cpp: In function ‘int main()’: 
testlambda.cpp:25:9: error: no match for call to ‘(main()::<lambda()>)()’ 
    lambda(); 
     ^
testlambda.cpp:22:29: note: candidate: template<class T> main()::<lambda()> 
    auto lambda=[]<typename T>() { 
          ^
testlambda.cpp:22:29: note: template argument deduction/substitution failed: 
testlambda.cpp:25:9: note: couldn't deduce template parameter ‘T’ 
    lambda(); 
     ^

Như bạn có thể thấy, một ứng cử viên là template<class T> main()::<lambda()>, nhưng điều này không biên dịch hoặc là:

lambda<int>()->error: expected primary-expression before ‘int’

Vì vậy, câu hỏi của tôi là: Cách chính thức, tuân thủ tiêu chuẩn để thực hiện việc này, nếu có? Tôi thực sự hy vọng con trỏ hack không phải là cách duy nhất. Nó có vẻ thực sự vụng về để sử dụng trong mã thực.

Tôi đang sử dụng G ++ (GCC 5.4.0) làm trình biên dịch của mình. Tôi cũng đang sử dụng tiêu chuẩn C++ 14 như -std=c++14.

+1

Thường thì nó được thực hiện dưới dạng 'mẫu struct được nhập {typedef T type; }; '. Vượt qua nó và sử dụng 'typename decltype (param) :: type'. –

Trả lời

7

Bạn có thể vượt qua một mẫu biến của một loại thẻ trống:

template <class T> struct tag_t { using type = T; }; 
template <class T> 
constexpr tag_t<T> tag{}; 

Bạn có thể viết lambda của bạn như:

auto lambda = [](auto type) { 
    return static_cast<typename decltype(type)::type>(foo); 
}; 

hoặc

auto lambda = [](auto type) -> typename decltype(type)::type { 
    return foo; 
}; 

và gọi nó như thế :

lambda(tag<int>); // 11 
lambda(tag<unsigned>); // 22 
+0

Chà. Nó hoạt động. Đó là một giải pháp thực sự tốt đẹp, ngay cả khi một đối số, đó không phải là một giá trị thực sự, vẫn cần phải được thông qua. Khái niệm templated 'constexpr's là một cái gì đó mới đối với tôi, tôi sẽ phải đọc về nó. Và tại sao nó là một 'typename' là cần thiết để làm cho nó hoạt động? Không nên trình biên dịch khi giải quyết mẫu 'thấy' rằng 'decltype (type) :: type' thực sự là một loại và không phải là một giá trị? Rất cám ơn, điều này giải quyết vấn đề của tôi một cách độc đáo. – negamartin

+1

@negamartin Xem [câu hỏi này] (http://stackoverflow.com/q/610245/2069064) – Barry

+0

Ồ, tôi hiểu. Một chút dài để gõ, nhưng một 'sử dụng' sẽ có thể bí danh nó thành một tên ngắn hơn. – negamartin

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