Cách tạo typedef có điều kiện trong C ++


89

Tôi đang cố gắng làm điều gì đó như sau:

#include <iostream>
#include <random>

typedef int Integer;

#if sizeof(Integer) <= 4
    typedef std::mt19937     Engine;
#else
    typedef std::mt19937_64  Engine;
#endif

int main()
{
    std::cout << sizeof(Integer) << std::endl;
    return 0;
}

nhưng tôi gặp lỗi này:

error: missing binary operator before token "("

Làm thế nào tôi có thể tạo chính xác typedef có điều kiện?


25
Bộ tiền xử lý không biết gì về sizeofhoặc các cấu trúc C ++ khác. Nó chắc chắn không biết về những thứ do chính bạn tạo ra typedef, vì nó thậm chí còn chưa được phân tích cú pháp.
Các cuộc đua ánh sáng trong quỹ đạo vào

2
Bạn có thể sử dụng enable_ifhoặc conditionalđể xác định typedef có điều kiện, nhưng bạn không thể sử dụng bộ tiền xử lý cho việc đó.
Bartek Banachewicz,

1
@LightnessRacesinOrbit: Tiền xử lý và biên dịch được tích hợp trong GCC, vì vậy không chỉ chắc chắn rằng mã xử lý phần mềm không biết về các định nghĩa kiểu do người dùng tạo mà còn sai trong trường hợp GCC. Lý do sizeofkhông thể hoạt động trong điều kiện tiền xử lý là vì ngôn ngữ được định nghĩa theo cách đó, không phải do cách triển khai hoạt động.
Eric Postpischil

1
@LightnessRacesinOrbit: Các giai đoạn dịch xác định cú pháp và ngữ nghĩa, không phải thứ tự xử lý. Theo C ++ 2011 (N3092) 2.2 [lex.phases] lưu ý 11, "Việc triển khai phải hoạt động như thể các giai đoạn riêng biệt này xảy ra, mặc dù trong thực tế, các giai đoạn khác nhau có thể được xếp lại với nhau." Quan điểm của tôi về GCC là có liên quan vì nó chứng minh rằng tuyên bố của bạn rằng đây là cách triển khai hoạt động là sai. Nói cách khác, nhận xét của bạn tuyên bố rằng một phương pháp triển khai cụ thể ngăn cản điều này. Nhưng nó không phải là việc thực hiện ngăn cản điều này (chúng tôi có thể làm điều đó); nó là định nghĩa ngôn ngữ.
Eric Postpischil

1
@Eric: Tôi không có ý yêu cầu bất cứ điều gì về việc triển khai. Tôi chắc chắn không đề cập đến bất kỳ điều gì cụ thể. Nhận xét của tôi đã nêu một hành vi tuân theo quy tắc as-if, cũng như của bạn. Tôi không nghĩ rằng chúng ta thực sự không đồng ý về bất cứ điều gì ở đây - khả năng hiểu ngôn ngữ của bạn cũng có thể đến từ tấm gương. :)
Lightness Races ở Orbit

Câu trả lời:


139

Sử dụng std::conditionalmeta-function từ C ++ 11.

#include <type_traits>  //include this

typedef std::conditional<sizeof(int) <= 4,
                         std::mt19937,
                         std::mt19937_64>::type Engine;

Lưu ý rằng nếu loại mà bạn sử dụng sizeoflà tham số mẫu T, thì bạn phải sử dụng typenamenhư sau:

typedef typename std::conditional<sizeof(T) <= 4, // T is template parameter
                                  std::mt19937,
                                  std::mt19937_64>::type Engine;

Hoặc làm cho Enginephụ thuộc vào Tnhư:

template<typename T>
using Engine = typename std::conditional<sizeof(T) <= 4, 
                                         std::mt19937,
                                         std::mt19937_64>::type;

Điều đó rất linh hoạt , vì bây giờ bạn có thể sử dụng nó như:

Engine<int>  engine1;
Engine<long> engine2;
Engine<T>    engine3; // where T could be template parameter!

4
+1 Vấn đề nhỏ: Kiểm tra sizeof(int) <= 4có lẽ không phải là cách rất dễ di chuyển vì trên máy Windows 64-bit, trình biên dịch GCC (MinGW) x64 cung cấp sizeof(int) = sizeof(long) = 4. Một cách tốt hơn sẽ là sizeof(void*) <= 4.
Legends2k,

@ Legends2k: Ý bạn là Engine<void*> engine4;? ;-)
Nawaz

2
@Nawaz: Tất nhiên là không :) Ý tôi là std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>trong đoạn mã đầu tiên.
Legends2k,

1
@ Legends2k: Tại sao bạn sẽ sử dụng nó nếu tôi đã cung cấp cho bạn Engine<void*>? : P
Nawaz

@Nawaz: Haha ... đúng là như vậy. Tuy nhiên, tôi nghĩ rằng OP có lẽ nên biết cạm bẫy trong việc phát hiện các kiến trúc dựa trên kích thước của một int:)
legends2k

35

Sử dụng std::conditionalbạn có thể làm như vậy:

using Engine = std::conditional<sizeof(int) <= 4, 
                               std::mt19937, 
                               std::mt19937_64
                               >::type;

Nếu bạn muốn làm một typedef, bạn cũng có thể làm điều đó.

typedef std::conditional<sizeof(int) <= 4, 
                         std::mt19937, 
                         std::mt19937_64
                         >::type Engine

Không cần typenameở đây
gx_

@gx_ Vâng, đã quen với việc đặt nó ở đó khi làm việc với các mẫu, không phải các loại cụ thể.
Rapptz

1
@LightnessRacesinOrbit Chà, tôi đã sửa nó một chút.
Rapptz

5

Nếu bạn không có sẵn C ++ 11 (mặc dù có vẻ như bạn có nếu định sử dụng std::mt19937), thì bạn có thể triển khai điều tương tự mà không cần hỗ trợ C ++ 11 bằng cách sử dụng Thư viện siêu lập trình Boost (MPL) . Đây là một ví dụ có thể tổng hợp:

#include <boost/mpl/if.hpp>
#include <iostream>
#include <typeinfo>

namespace mpl = boost::mpl;

struct foo { };
struct bar { };

int main()
{
    typedef mpl::if_c<sizeof(int) <= 4, foo, bar>::type Engine;

    Engine a;
    std::cout << typeid(a).name() << std::endl;
}

Điều này in tên bị lệch của footrên hệ thống của tôi, intở đây là 4 byte.


1
Tại sao bạn không sử dụng if_cthay thế? Sẽ phải dễ dàng hơn để viết (và hiểu): mpl::if_c<sizeof(int)<=4, foo, bar>::type. Phải không?
Nawaz

1
@Nawaz: Thật vậy, điều đó tốt hơn theo một số cách. Tôi đã quên về mpl::if_c. Tôi đã cập nhật ví dụ để sử dụng cách tiếp cận đó thay thế.
Jason R
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.