8

Các mã sau đây làm việc với GCC và Clang, nhưng không phải với Visual C++:Làm cách nào để trì hoãn việc khởi tạo thành viên dữ liệu tĩnh trong Visual C++?

#include <type_traits> 

struct MyType { 
    static constexpr std::size_t it = 10; 
}; 

struct MyType2 { 
}; 

template<typename T> 
struct Type2 { 
    static constexpr std::size_t it = T::it; 
}; 

int main() { 
    Type2<MyType> t1; 
    Type2<MyType2> t2; // Visual C++ complains that MyType2::it doesn't exist 
    (void) t1; 
    (void) t2; 
} 

Theo phần 14.7.1 của tiêu chuẩn:

... việc khởi tạo (và bất kỳ bên liên quan -effects) của một thành viên dữ liệu tĩnh không xảy ra trừ khi các thành viên dữ liệu tĩnh là chính nó sử dụng theo cách mà đòi hỏi định nghĩa của các thành viên dữ liệu tĩnh tồn tại

Vì vậy, có vẻ như đây là một lỗi trong Visual C++; nó phải chấp nhận mã này.

Bất kể, tôi vẫn muốn có thể thực hiện việc này bằng Visual C++. Cách dễ nhất để cho phép Visual C++ hoạt động, mà không thay đổi cú pháp truy cập biến thành viên là gì? Tôi cũng yêu cầu rằng Type2<T>::it không tồn tại khi T::it không tồn tại hoặc có thể SFINAE không tồn tại trong số Type2<T>::it.


này thay đổi cú pháp, vì vậy tôi không muốn nó:

template<typename T> 
struct Type2 { 
    template<typename = void> 
    static constexpr std::size_t it = T::it; 
}; 

// Type2<MyType>::it<> 

Đây là rất nhiều công việc, bởi vì lớp học của tôi sẽ chứa nhiều hơn một constexpr biến đơn giản:

template<typename T, typename = void> 
struct Type2 { 
}; 

template<typename T> 
struct Type2<T, decltype((void) T::it)> { 
    static constexpr std::size_t it = T::it; 
}; 
+0

Điều này dường như hoạt động: http://coliru.stacked-crooked.com/a/1fd7999053358d43 –

+0

Bạn đang đọc quá nhiều vào tiêu chuẩn. Nó không nói rằng khởi tạo không hợp lệ được cho phép nếu thành viên không bao giờ được sử dụng. Ví dụ. 'it = (abort(), 0)' khác với ví dụ của bạn vì nó tạo ra lỗi thời gian chạy và không phải là lỗi biên dịch. –

Trả lời

2

Bạn có thể kế thừa it (khi nó tồn tại) thay vì khai báo trực tiếp:

template<typename T, typename = void> 
struct ItHolder 
{}; 

template<typename T> 
struct ItHolder<T, decltype((void) T::it)> { 
    static constexpr std::size_t it = T::it; 
}; 


template<typename T> 
struct Type2 : ItHolder<T> { 
}; 

Nói cách khác, chỉ cần lấy những gì bạn đã đề xuất và kết hợp nó bằng cách sử dụng một lớp khác.

+0

@Justin Đó là một yêu cầu bạn nên thêm vào câu hỏi, sau đó. Nó không rõ ràng chút nào từ từ ngữ hiện tại của nó. – Angew

+0

@Justin Các giải pháp vẫn còn dễ dàng để thích nghi, mặc dù. Đã cập nhật câu trả lời. – Angew

+0

Điều này rất hay. So với việc đảo ngược thừa kế (có một 'Type2Base' có chứa việc thực hiện bình thường, và' Type2' gần như là trong câu hỏi), tôi thích nó theo cách này. Đối với một, nó làm cho nó dễ dàng để mở rộng việc thực hiện cho nhiều thành viên, và nó cũng làm cho nó dễ dàng hơn để giữ các thông số mẫu của loại dự định của tôi giống như tôi dự định ban đầu. – Justin

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