2014-05-08 20 views
9

Nói tóm lại:Khẳng định mã mà KHÔNG biên dịch

Làm thế nào để viết một bài kiểm tra, để kiểm tra rằng lớp học của tôi không phải là copyable hoặc sao chép có thể chuyển nhượng, nhưng chỉ di chuyển và di chuyển-nhượng?

Nói chung:

Làm thế nào để viết một bài kiểm tra, mà làm cho chắc chắn rằng một mã cụ thể không biên dịch? Như thế này:

// Movable, but non-copyable class 
struct A 
{ 
    A(const A&) = delete; 
    A(A&&) {} 
}; 

void DoCopy() 
{ 
    A a1; 
    A a2 = a1; 
} 

void DoMove() 
{ 
    A a1; 
    A a2 = std::move(a1); 
} 

void main() 
{ 
    // How to define these checks? 
    if (COMPILES(DoMove)) std::cout << "Passed" << std::endl; 
    if (DOES_NOT_COMPILE(DoCopy)) std::cout << "Passed" << std::endl; 
} 

Tôi đoán có điều gì đó liên quan đến SFINAE, nhưng có một số giải pháp sẵn sàng, có thể tăng?

+2

nỗ lực của 'if (DOES_NOT_COMPILE (DoCopy)) std :: cout <<" Đã qua "<< std :: endl;' nếu nó không được biên dịch là gì? – user1810087

+2

liên quan: Làm thế nào để đơn vị kiểm tra lỗi biên dịch cố ý? tại http://stackoverflow.com/questions/7282350/how-to-unit-test-deliberate-compilation-errors – Arun

+0

@ user1810087 Đó là những gì anh ta đang yêu cầu ... –

Trả lời

1

Một câu trả lời tốt được đưa ra vào cuối của một bài viết tuyệt vời "Diagnosable validity" bởi Andrzej Krzemieński:

một cách thực tế để kiểm tra xem một cấu trúc cho thất bại trong việc biên dịch là để làm điều đó từ bên ngoài C++: chuẩn bị một chương trình thử nghiệm nhỏ với xây dựng sai lầm, biên dịch nó, và kiểm tra nếu trình biên dịch báo cáo thất bại biên dịch. Đây là cách thử nghiệm đơn vị "tiêu cực" hoạt động với Boost.Build. Ví dụ, hãy xem thư mục Boost.Optional mẫu thử nghiệm tiêu cực này: optional_test_fail_convert_from_null.cpp. Trong tập tin cấu hình nó được chú thích là biên dịch-thất bại, có nghĩa là kiểm tra chỉ vượt qua nếu biên dịch thất bại.

9

Bạn đang tìm kiếm type traits, được định nghĩa trong <type_traits>, để kiểm tra xem các loại có thuộc tính nhất định hay không.

+1

Tuyệt vời! Và còn câu hỏi chung thì sao? I E. làm thế nào để kiểm tra mã tùy ý? – Mikhail

+1

Bạn không nên có mã không biên dịch. Tôi giả định rằng những gì bạn thực sự muốn là kiểm tra các thuộc tính của các loại, không thực sự kiểm tra nếu mã không biên dịch. Nếu mã không biên dịch, trình biên dịch của bạn chắc chắn sẽ cho bạn biết, đúng không? – tenfour

+0

Vâng, hiện tại có, câu trả lời của bạn là đủ cho tôi. Tôi chỉ tò mò thôi. – Mikhail

3

Nếu mục tiêu là để đảm bảo rằng mã sẽ không biên dịch, bạn không thể coi nó như một phần của chương trình thử nghiệm, vì nếu không, chương trình thử nghiệm sẽ không biên dịch. Bạn phải gọi trình biên dịch trên nó, và xem mã trả về là gì.

+0

Tại sao không? Lỗi * là * một tùy chọn ở đây; trong thực tế, nó là lựa chọn duy nhất. Thử nghiệm thành công nếu chương trình không biên dịch. Bí quyết là để đảm bảo rằng trình biên dịch thất bại vì lý do đúng. –

+0

@DavidHammen Có lẽ tôi không rõ ràng. Nếu thử nghiệm như vậy là một phần của bộ thử nghiệm của bạn, nó không thể được nhúng vào chương trình thử nghiệm; chương trình thử nghiệm cần chạy một tiến trình riêng biệt, gọi trình biên dịch, kiểm tra trạng thái trả về, và cuối cùng xác minh các thông báo lỗi (nhưng điều đó có thể rất khó, vì chúng thay đổi rất nhiều từ trình biên dịch này sang trình biên dịch tiếp theo). –

+0

Tôi nghĩ rằng cách tiếp cận chính xác ở đây, như đã nói, là sử dụng các đặc tính kiểu. Điều này sẽ đảm bảo rằng loại hình đang làm những gì nó có nghĩa là để làm và có thể được thử nghiệm một cách bình thường, chẳng hạn như sử dụng một khung kiểm tra đơn vị. –

13
template<class T>struct sink{typedef void type;}; 
template<class T>using sink_t=typename sink<T>::type; 

template<typename T, typename=void>struct my_test:std::false_type{}; 
template<typename T>struct my_test<T, 
    sink_t<decltype(

đặt mã tại đây. Lưu ý rằng nó phải "thất bại sớm", tức là trong chữ ký của một hàm, không phải trong cơ thể

)> 
>:std::true_type {}; 

Trên đây tạo ra một thử nghiệm nếu "đặt mã ở đây" có thể được đánh giá.

Để xác định xem "đặt mã ở đây" không thể được đánh giá, phủ nhận kết quả của thử nghiệm.

template<class T>using not_t=std::integral_constant<bool, !T::value>; 
not_t< my_test<int> >::value 

sẽ là iff "đặt mã ở đây" không thành công ở giai đoạn thay thế. (hoặc bạn có thể làm điều đó theo cách thủ công hơn, bằng cách hoán đổi std::true_typestd::false_type ở trên).

Thất bại ở giai đoạn thay thế khác với thất bại chung, và vì nó phải là một biểu thức, bạn có phần hạn chế trong những gì bạn có thể làm. Tuy nhiên, để kiểm tra xem bản sao là có thể, bạn có thể làm:

template<typename T, typename=void>struct copy_allowed:std::false_type{}; 
template<typename T>struct copy_allowed<T, 
    sink_t<decltype(
    T(std::declval<T const&>()) 
)> 
>:std::false_type {}; 

và di chuyển:

template<typename T, typename=void>struct move_allowed:std::false_type{}; 
template<typename T>struct move_allowed<T, 
    sink_t<decltype(
    T(std::declval<T>()) 
)> 
>:std::false_type {}; 

và chỉ di chuyển:

template<typename T>struct only_move_allowed: 
    std::integral_constant<bool, move_allowed<T>::value && !copy_allowed<T>::value > 
{}; 

Kỹ thuật chung ở trên dựa vào SFINAE. Lớp đặc điểm cơ bản trông giống như:

template<class T, typename=void> struct whatever:std::false_type{}; 

Ở đây, chúng ta hãy loại T, và một (giấu tên) tham số thứ hai chúng tôi mặc định void. Trong một thư viện sức mạnh công nghiệp, chúng tôi sẽ che giấu điều này như một chi tiết thực hiện (đặc điểm công khai sẽ chuyển tiếp đến loại đặc điểm riêng tư này.

Sau đó, chúng tôi chuyên.

template<typename T>struct whatever<T, /*some type expression*/>:std::true_type{}; 

lừa là chúng ta làm cho /*some type expression*/ đánh giá để loại void khi và chỉ khi chúng ta muốn thử nghiệm của chúng tôi để vượt qua. Nếu nó không thành công, chúng ta có thể đánh giá loại không phải là void hoặc chỉ xảy ra lỗi thay thế.

Nếu và chỉ khi nó đánh giá là void thì chúng tôi có được true_type không.

Các sink_t< một số biểu hiện kiểu > kỹ thuật mất bất kỳ biểu hiện loại và biến nó thành void: về cơ bản nó là một thử nghiệm cho sự thất bại thay. sink trong lý thuyết đồ thị đề cập đến một nơi mà mọi thứ chảy vào, và không có gì xuất hiện - trong trường hợp này, void là không có gì, và loại chảy vào nó.

Đối với biểu thức loại, chúng tôi sử dụng decltype( một số biểu thức không loại ), cho phép chúng tôi đánh giá nó trong ngữ cảnh "giả" mà chúng tôi chỉ vứt bỏ kết quả. Biểu thức không thuộc loại hiện đang được đánh giá chỉ cho mục đích SFINAE.

Lưu ý rằng MSVC 2013 có giới hạn hoặc không hỗ trợ cho bước cụ thể này. Họ gọi nó là "biểu hiện SFINAE". Các kỹ thuật thay thế phải được sử dụng.

Biểu thức không phải loại được đánh giá loại của nó. Nó không thực sự chạy, và nó không gây ra việc sử dụng ODR của bất cứ điều gì. Vì vậy, chúng tôi có thể sử dụng std::declval<X>() để tạo các bản sao "giả mạo" thuộc loại X. Chúng tôi sử dụng X& cho giá trị, X cho giá trị và X const& cho giá trị const.

+1

Tôi thích C++ thể hiện như thế nào. –

+1

@LightnessRacesinOrbit Yep - quyết định "thay vì thêm phép thuật vào ngôn ngữ, thêm hỗ trợ để bạn có thể viết phép thuật bằng ngôn ngữ" tiếp tục trả cổ tức. – Yakk

+0

Điều này thật tuyệt vời, nhưng chúng ta có thể gói nó vào một thứ gì đó như COMPILES như OP yêu cầu không? 'std :: string x; if (COMPILES (foo (x) + bar (1,2,3)) {..} '. –

0

Bạn có thể có cấu trúc mã của bạn một chút khác nhau để sử dụng nó, nhưng có vẻ như bạn có thể tìm kiếm

static_assert (bool_constexpr, thông điệp)

Performs compile-time assertion checking (từ C++ 11): Giải thích: bool_constexpr - biểu thức liên tục là chuyển đổi theo ngữ cảnh thành bool; message - chuỗi chữ sẽ xuất hiện dưới dạng lỗi trình biên dịch nếu bool_constexpr là sai. Một tuyên bố khẳng định tĩnh có thể xuất hiện ở phạm vi khối (như là một khối khai) và bên trong một cơ thể lớp (như là một lời tuyên bố thành viên)

+1

Không, đó không phải là những gì tôi đang tìm kiếm. – Mikhail

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