2012-02-15 8 views
14

Tôi cần lấy loại được cung cấp khi tạo mẫu. Hãy xem xét ví dụ sau:decltype và toán tử phạm vi trong C++

template <typename T> struct Foo 
{ 
    typedef T TUnderlying; 
}; 

static Foo<int> FooInt; 

class Bar 
{ 
public: 
    auto Automatic() -> decltype(FooInt)::TUnderlying 
    { 
    return decltype(FooInt)::TUnderlying(); 
    } 
}; 

int main() 
{ 
    Bar bar; 
    auto v = bar.Automatic(); 
    return 0; 
} 

Sự cố với mã này đang sử dụng toán tử phạm vi cùng với decltype. Visual C++ 2010 phàn nàn như thế này:

lỗi C2039: 'TUnderlying': không phải là thành viên của '`namespace toàn cầu''

tôi thu thập được một số thông tin về chủ đề này trên Wikipedia:

Trong khi bình luận về Dự thảo Ủy ban chính thức cho C++ 0x, cơ quan thành viên ISO Nhật Bản lưu ý rằng "một toán tử phạm vi (: :) không thể được áp dụng cho decltype, nhưng nó nên được. Nó sẽ hữu ích trong trường hợp để có được kiểu thành viên (lồng nhau- loại) từ một ví dụ như sau ": [16]

vector<int> v; 
decltype(v)::value_type i = 0; // int i = 0; 

Điều này, và các vấn đề tương tự đã được giải quyết bởi David Vandevoorde, và được bỏ phiếu vào bài báo làm việc tháng 3 năm 2010.

Vì vậy, tôi nghĩ rằng Visual C++ 2010 không thực hiện điều này. Tôi đã đưa ra cách giải quyết này:

template <typename T> struct ScopeOperatorWorkaroundWrapper 
{ 
    typedef typename T::TUnderlying TTypedeffedUnderlying; 
}; 

auto Automatic() -> ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying 
{ 
    return ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying(); 
} 

Tôi có bỏ lỡ bất kỳ giải pháp nào thanh lịch và tiết kiệm hơn không?

+1

bạn đã thử 'FooInt :: TUnderlying' thay vì 'decltype (FooInt) :: TUnderlying'?Tôi không thấy những gì bạn mong đợi để đạt được thông qua 'decltype' ở đây. – sellibitze

+0

Tất cả những gì bạn thêm là 'ScopeOperatorWorkaroundWrapper <>', tôi không biết số lượng "ít tiết" hơn bạn có thể muốn. Sau khi tất cả, một workaround của nó. – PlasmaHH

Trả lời

12

Điều này minh bạch thay thế từ khóa decltype bằng cách giải quyết dựa trên mẫu. Khi bạn không còn cần phải hỗ trợ MSVC2010 bạn có thể loại bỏ các định nghĩa vĩ mô mà không thay đổi bất kỳ mã dùng:

#if _MSC_VER == 1600 
#include <utility> 
#define decltype(...) \ 
    std::identity<decltype(__VA_ARGS__)>::type 
#endif 

nào cho phép này để biên dịch và làm việc trên MSVC10:

std::vector<int> v; 
decltype(v)::value_type i = 0; 

Lưu ý rằng std::identity không phải là một phần của tiêu chuẩn C++, nhưng nó là an toàn để dựa vào nó ở đây như workaround được giới hạn trong một trình biên dịch bao gồm std::identity trong việc thực hiện thư viện chuẩn của nó.

+0

Sử dụng macro Vd để khắc phục sự cố dấu phẩy. '#define decltype (...) detail :: type_helper :: type'. Lưu ý rằng 'decltype (x)' có thể không giống với 'decltype ((x))', do đó, 't2' của bạn có thể dẫn đến một kiểu khác. – kennytm

+0

@KennyTM: Cảm ơn, tôi đã kết hợp điều đó vào câu trả lời. –

+0

Tôi thấy giải pháp này là ít xâm nhập nhất và dễ nhất để loại bỏ khi trình biên dịch thực hiện tính năng này. Cảm ơn bạn đã dành thời gian! –

2

Cách giải quyết có vẻ tương đối tốt nhưng không thể mở rộng và tên là khủng khiếp . Tại sao không sử dụng id?

template <typename T> 
struct id { 
    typedef T type; 
}; 

Và sau đó:

id<decltype(FooInt)>::type::TUnderlying; 

chưa được kiểm tra, nhưng nên làm việc.


Như trong, quá dài dòng và mặc dù họ mô tả rằng đó là một cách giải quyết khác, đây có thể là không cần thiết và không phải là một thông tin hữu ích trong hầu hết các tình huống.

+0

Cảm ơn bạn đã nhập. Về tên - đó chỉ là một ví dụ và tôi thừa nhận rằng các tên có thể ngắn hơn một chút, nhưng tôi thích các tên nắm bắt được lý do của vấn đề. Đồng bằng "id" sẽ sau này (khi tôi quên nó) nói với tôi không có gì rằng nó chỉ là một hack tạm thời. Tôi nghĩ rằng nó không phải là cần thiết với các tính năng tự động hoàn thành hiện đại ở bàn tay để làm cho mã của tôi khó hiểu với chữ viết tắt khó hiểu. –

+0

@Milan 'id' là tên chính xác cho biến thể này (đó là [chức năng nhận dạng] (http://en.wikipedia.org/wiki/Identity_function)), và tôi nghĩ nó thực sự đã được định nghĩa ở đâu đó trong C++ 11 chính xác để phục vụ như là một cách giải quyết cú pháp ở những nơi mà một loại đồng bằng không thể được sử dụng. Dù bằng cách nào, bạn * nên * sử dụng tên này vì đó là tên được thiết lập cho khái niệm này và thường được hiểu. Nó không có nghĩa là "viết tắt khó hiểu" (mà tôi đồng ý không bao giờ nên được sử dụng). –

+0

@Milan Nhưng kể từ khi bạn đề cập đến hoàn thành tự động: lý do để tránh tên dài là * không * mà phải mất nhiều thời gian để gõ chúng (tự động hoàn thành giúp ở đây, đúng). Nhưng nó cũng làm cho mã * không thể đọc được * và các tên ngắn gọn (ngắn gọn nhưng chính xác) luôn được ưa thích. –

0

Là một thay thế, bạn có thể dễ dàng kéo các loại bằng cách sử dụng một hàm template helper:

template <typename T> struct Foo 
{ 
    typedef T TUnderlying; 
}; 

static Foo<int> FooInt; 

template <typename T> 
typename Foo<T>::TUnderlying foo_underlying(Foo<T> const &) 
{ 
    return typename Foo<T>::TUnderlying(); 
} 

class Bar 
{ 
public: 
// auto Automatic() -> decltype(FooInt)::Underlying 
// { 
//  return decltype(FooInt)::Underlying; 
// } 
    auto Automatic() -> decltype(foo_underlying(FooInt)) 
    { 
     return foo_underlying(FooInt); 
    } 
}; 

int main() 
{ 
    Bar bar; 
    auto v = bar.Automatic(); 
}