วิธีสร้าง typedef ตามเงื่อนไขใน C ++


90

ฉันกำลังพยายามทำสิ่งนี้:

#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;
}

แต่ฉันได้รับข้อผิดพลาดนี้:

error: missing binary operator before token "("

ฉันจะทำให้พิมพ์ดีดตามเงื่อนไขถูกต้องได้อย่างไร


25
ตัวประมวลผลก่อนไม่รู้อะไรเลยsizeofหรือโครงสร้าง C ++ อื่น ๆ แน่นอนว่ามันไม่รู้เกี่ยวกับสิ่งที่คุณสร้างขึ้นเองtypedefเนื่องจากยังไม่ได้รับการแยกวิเคราะห์
Lightness Races ใน Orbit

2
คุณสามารถใช้enable_ifหรือconditionalกำหนด typedef แบบมีเงื่อนไขได้ แต่คุณไม่สามารถใช้ตัวประมวลผลล่วงหน้าสำหรับสิ่งนั้นได้
Bartek Banachewicz

1
@LightnessRacesinOrbit: การประมวลผลล่วงหน้าและการคอมไพล์รวมอยู่ใน GCC ดังนั้นจึงไม่เพียง แต่ไม่แน่ใจว่ารหัสการประมวลผลซอฟต์แวร์ไม่ทราบเกี่ยวกับคำจำกัดความประเภทที่ผู้ใช้สร้างขึ้น แต่เป็นที่ทราบกันดีว่าเป็นเท็จในกรณีของ GCC สาเหตุที่sizeofไม่สามารถทำงานในเงื่อนไขของตัวประมวลผลล่วงหน้าได้เนื่องจากภาษาถูกกำหนดด้วยวิธีนั้นไม่ใช่เพราะวิธีการทำงานของการนำไปใช้งาน
Eric Postpischil

1
@LightnessRacesinOrbit: ขั้นตอนการแปลกำหนดไวยากรณ์และความหมายไม่ใช่ลำดับของการประมวลผล ตาม C ++ 2011 (N3092) 2.2 [lex.phases] หมายเหตุ 11 "การนำไปใช้งานต้องทำงานราวกับว่ามีขั้นตอนแยกกันเหล่านี้เกิดขึ้นแม้ว่าในทางปฏิบัติอาจมีการพับขั้นตอนต่างๆเข้าด้วยกัน" ประเด็นของฉันเกี่ยวกับ GCC มีความเกี่ยวข้องเนื่องจากแสดงให้เห็นว่าคำกล่าวอ้างของคุณว่านี่เป็นวิธีการทำงานที่ผิด กล่าวอีกนัยหนึ่งความคิดเห็นของคุณอ้างว่าวิธีการใช้งานเฉพาะป้องกันสิ่งนี้ แต่มันไม่ได้ดำเนินการที่ป้องกันไม่ให้นี้ (เราสามารถทำมันได้); มันคือความหมายของภาษา
Eric Postpischil

1
@ เอริก: ฉันไม่ได้ตั้งใจที่จะเรียกร้องอะไรเกี่ยวกับการใช้งาน แต่อย่างใด ฉันไม่ได้พูดถึงเรื่องใดเรื่องหนึ่งเป็นพิเศษ ความคิดเห็นของฉันระบุพฤติกรรมที่อยู่ภายใต้กฎ as-if เช่นเดียวกับของคุณ ฉันไม่คิดว่าเราไม่เห็นด้วยกับอะไรที่นี่ - กฎหมายภาษาของคุณอาจมาจากกระจกเงา :)
Lightness Races in Orbit

คำตอบ:


139

ใช้std::conditionalmeta-function จาก C ++ 11

#include <type_traits>  //include this

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

โปรดทราบว่าหากประเภทที่คุณใช้sizeofเป็นพารามิเตอร์เทมเพลตให้พูดTคุณต้องใช้typenameเป็น:

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

หรือEngineขึ้นอยู่กับT:

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

มีความยืดหยุ่นเพราะตอนนี้คุณสามารถใช้เป็น:

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

4
+1 ไมเนอร์ nitpick: การตรวจสอบsizeof(int) <= 4อาจจะไม่ได้เป็นวิธีแบบพกพามากตั้งแต่บนเครื่อง Windows 64 บิต GCC (MinGW) x 64 sizeof(int) = sizeof(long) = 4คอมไพเลอร์จะช่วยให้ วิธีที่ดีกว่าก็sizeof(void*) <= 4คือ
legends2k

@ legends2k: คุณหมายถึงEngine<void*> engine4;? ;-)
นาวาซ

2
@Nawaz: ไม่แน่นอน :) ฉันหมายถึงstd::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>ข้อมูลโค้ดแรก
legends2k

1
@ legends2k: ทำไมคุณถึงใช้สิ่งนั้นถ้าฉันให้คุณEngine<void*>? : P
Nawaz

@Nawaz: ฮ่าฮ่า ... ที่แท้ อย่างไรก็ตามฉันคิดว่า OP น่าจะรู้ข้อผิดพลาดในการตรวจจับสถาปัตยกรรมตามขนาดของint:)
legends2k

35

โดยใช้std::conditionalคุณสามารถทำได้ดังนี้:

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

ถ้าอยากทำ a typedefก็ทำได้เช่นกัน

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

typenameที่นี่ไม่จำเป็น
gx_

@gx_ ใช่เคยวางไว้ที่นั่นจากการทำงานกับเทมเพลตไม่ใช่ประเภทคอนกรีต
Rapptz

1
@LightnessRacesinOrbit ดีฉันแก้ไขมันเล็กน้อย
Rapptz

5

หากคุณไม่ได้มี C ++ 11 ใช้ได้ (แม้ว่ามันจะดูเหมือนว่าคุณจะทำอย่างไรถ้าคุณวางแผนกำลังจะใช้std::mt19937) แล้วคุณสามารถดำเนินการในสิ่งเดียวกันโดยไม่ต้อง C ++ 11 สนับสนุนการใช้Boost metaprogramming ห้องสมุด (MPL) นี่คือตัวอย่างที่รวบรวมได้:

#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;
}

สิ่งนี้จะพิมพ์ชื่อที่แหลกเหลวfooในระบบของฉันintเป็น 4 ไบต์ที่นี่


1
ทำไมไม่ใช้if_cแทน มันจะต้องง่ายต่อการเขียน mpl::if_c<sizeof(int)<=4, foo, bar>::type(และเข้าใจ): ไม่ใช่เหรอ?
Nawaz

1
@Nawaz: แน่นอนว่าดีกว่าในหลาย ๆ ด้าน ฉันลืมไปmpl::if_cแล้ว ฉันอัปเดตตัวอย่างเพื่อใช้แนวทางนั้นแทน
Jason R
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.