ส่งต่อการประกาศของ typedef ใน C ++


235

ทำไมคอมไพเลอร์ไม่ให้ฉันไปข้างหน้าเพื่อประกาศ typedef?

ถ้าเป็นไปไม่ได้แนวทางปฏิบัติที่ดีที่สุดในการทำให้ต้นไม้รวมของฉันเล็ก ๆ คืออะไร?

คำตอบ:


170

คุณสามารถพิมพ์ล่วงหน้าได้ แต่ต้องทำ

typedef A B;

ก่อนอื่นคุณต้องส่งต่อประกาศA:

class A;

typedef A B;

11
+1 ในที่สุดเพราะในขณะที่คุณไม่สามารถ "forward-typedef" (เช่นคุณไม่สามารถเขียน "typedef A;") คุณสามารถทำสิ่งที่ OP ต้องการให้สำเร็จได้โดยใช้เคล็ดลับของคุณด้านบน
j_random_hacker

9
แต่ระวังหากการเปลี่ยนแปลง typedef คุณอาจเปลี่ยนการประกาศไปข้างหน้าทั้งหมดซึ่งคุณอาจพลาดหาก typedef เก่าและใหม่ที่ใช้ประเภทที่มีอินเตอร์เฟซเดียวกัน
คณิตศาสตร์

50
โดยทั่วไปนี่ไม่ใช่วิธีแก้ปัญหาที่มีประโยชน์ ตัวอย่างเช่นถ้าtypedefชื่อประเภทเทมเพลตหลายระดับที่ซับซ้อนโดยใช้การประกาศไปข้างหน้าด้วยวิธีนี้ค่อนข้างซับซ้อนและยาก ไม่ต้องพูดถึงว่ามันอาจต้องดำน้ำในรายละเอียดการใช้งานที่ซ่อนอยู่ในข้อโต้แย้งแม่แบบเริ่มต้น และทางออกสุดท้ายคือรหัสที่มีความยาวและไม่สามารถอ่านได้ (โดยเฉพาะเมื่อประเภทมาจากเนมสเปซต่างๆ) มีแนวโน้มที่จะเปลี่ยนแปลงในรูปแบบดั้งเดิม
Adam Badura

3
นอกจากนี้ยังแสดง "รายละเอียดการใช้งาน" (แม้ว่าจะยังไม่ครบถ้วน แต่ก็ยัง ... ) ในขณะที่แนวคิดที่อยู่เบื้องหลังการประกาศล่วงหน้าคือการซ่อนพวกเขา
Adam Badura

3
@windfinder: ทำเช่นนี้: template <class T> class A; typedef A <C> B;
milianw

47

สำหรับคนที่ชอบฉันที่กำลังมองไปข้างหน้าประกาศ C-style struct ที่กำหนดโดยใช้ typedef ในบางรหัส c ++ ฉันได้พบวิธีแก้ปัญหาที่ไปดังนี้ ...

// a.h
 typedef struct _bah {
    int a;
    int b;
 } bah;

// b.h
 struct _bah;
 typedef _bah bah;

 class foo {
   foo(bah * b);
   foo(bah b);
   bah * mBah;
 };

// b.cpp
 #include "b.h"
 #include "a.h"

 foo::foo(bah * b) {
   mBah = b;
 }

 foo::foo(bah b) {
   mBah = &b;
 }

4
@LittleJohn ปัญหาเกี่ยวกับการแก้ปัญหานี้ก็คือ dummy-name _bah ไม่ถือเป็นส่วนหนึ่งของ API สาธารณะ ดูรายละเอียดไฟล์ delcare
user877329

23

หากต้องการ "fwd ประกาศ typedef" คุณต้อง fwd ประกาศคลาสหรือ struct จากนั้นคุณสามารถพิมพ์ typedef ประกาศได้ คอมไพเลอร์ตัวเดียวกันหลายตัวนั้นเป็นที่ยอมรับได้

แบบยาว:

class MyClass;
typedef MyClass myclass_t;

แบบสั้น:

typedef class MyClass myclass_t;

สิ่งนี้แตกต่างจากคำถามที่ได้รับการโหวตมากที่สุดอย่างไร stackoverflow.com/a/804956/931303
Jorge Leitao

1
@ JorgeLeitãoคุณไม่เห็นว่ามันแตกต่างกันอย่างไร ไม่แสดงวิธีการทำในบรรทัดเดียว
Pavel P

17

ใน C ++ (แต่ไม่ใช่ C ธรรมดา) การพิมพ์ประเภทสองครั้งถูกต้องตามกฎหมายตราบใดที่คำจำกัดความทั้งสองเหมือนกันหมด :

// foo.h
struct A{};
typedef A *PA;

// bar.h
struct A;  // forward declare A
typedef A *PA;
void func(PA x);

// baz.cc
#include "bar.h"
#include "foo.h"
// We've now included the definition for PA twice, but it's ok since they're the same
...
A x;
func(&x);

34
การบำรุงรักษาไม่ไม่สิ่งนี้จะกัดคุณใน keister ไม่ช้าก็เร็ว
ทำเครื่องหมาย Storer

3
@MarkStorer อย่างน้อยคอมไพเลอร์จะตรวจจับความแตกต่างและสร้างข้อผิดพลาด ฉันตรวจสอบสิ่งนี้ด้วย Visual C ++
Alan

ดี แต่คุณจะกำหนดAเขตข้อมูลด้วยวิธีนี้เนื่องจากAว่างตามคำจำกัดความอย่างไร
Patrizio Bertoni

10

เนื่องจากต้องประกาศประเภทจึงจำเป็นต้องทราบขนาด คุณสามารถส่งต่อตัวชี้ไปยังประเภทหรือพิมพ์ตัวชี้ไปยังประเภท

หากคุณต้องการจริงๆคุณสามารถใช้ pimpl idiom เพื่อให้รวมลง แต่ถ้าคุณต้องการใช้ชนิดแทนที่จะเป็นตัวชี้คอมไพเลอร์จะต้องรู้ขนาดของมัน

แก้ไข: j_random_hacker เพิ่มคุณสมบัติที่สำคัญให้กับคำตอบนี้โดยทั่วไปแล้วขนาดจำเป็นต้องรู้ในการใช้งานประเภทนี้ แต่สามารถทำการประกาศล่วงหน้าได้หากเราต้องการทราบประเภทที่มีอยู่เท่านั้นเพื่อสร้างตัวชี้หรือการอ้างอิงถึง ชนิด เนื่องจาก OP ไม่ได้แสดงรหัส แต่บ่นว่ามันจะไม่คอมไพล์ฉันจึงสันนิษฐาน (อาจถูกต้อง) ว่า OP พยายามใช้งานประเภทนั้นไม่ใช่แค่อ้างถึง


35
การประกาศล่วงหน้าของประเภทชั้นเรียนประกาศประเภทเหล่านี้โดยไม่ทราบขนาด นอกจากนี้นอกเหนือจากความสามารถในการกำหนดพอยน์เตอร์และการอ้างอิงถึงชนิดที่ไม่สมบูรณ์ดังกล่าวแล้วฟังก์ชันสามารถประกาศได้ (แต่ไม่ได้กำหนดไว้) ซึ่งรับพารามิเตอร์และ / หรือคืนค่าประเภทนั้น ๆ
j_random_hacker

3
ขอโทษฉันไม่คิดว่าเป็นข้อสันนิษฐานที่ดี คำตอบนี้อยู่ข้างจุด นี่เป็นกรณีของการพิมพ์การประกาศล่วงหน้า
คุกกี้

6

การใช้การประกาศไปข้างหน้าแทนที่จะเป็นแบบเต็ม#includeจะทำได้ก็ต่อเมื่อคุณไม่ได้ตั้งใจจะใช้ตัวพิมพ์ (ในขอบเขตของไฟล์นี้) แต่เป็นตัวชี้หรือการอ้างอิง

ในการใช้ชนิดตัวแปลคอมไพเลอร์จะต้องรู้ขนาดของมัน - ด้วยเหตุนี้จึงต้องเห็นการประกาศอย่างเต็มรูปแบบ - จึง#includeจำเป็นต้อง ใช้แบบเต็ม

อย่างไรก็ตามขนาดของตัวชี้หรือการอ้างอิงเป็นที่ทราบกันโดยคอมไพเลอร์โดยไม่คำนึงถึงขนาดของ Pointee ดังนั้นการประกาศไปข้างหน้าก็เพียงพอแล้ว - มันประกาศชื่อตัวระบุชนิด

ที่น่าสนใจเมื่อใช้ตัวชี้หรือการอ้างอิงถึงclassหรือstructชนิดคอมไพเลอร์สามารถจัดการประเภทที่ไม่สมบูรณ์ซึ่งจะช่วยให้คุณไม่จำเป็นต้องส่งต่อชนิดของ Pointee ด้วย:

// header.h

// Look Ma! No forward declarations!
typedef class A* APtr; // class A is an incomplete type - no fwd. decl. anywhere
typedef class A& ARef;

typedef struct B* BPtr; // struct B is an incomplete type - no fwd. decl. anywhere
typedef struct B& BRef;

// Using the name without the class/struct specifier requires fwd. decl. the type itself.    
class C;         // fwd. decl. type
typedef C* CPtr; // no class/struct specifier 
typedef C& CRef; // no class/struct specifier 

struct D;        // fwd. decl. type
typedef D* DPtr; // no class/struct specifier 
typedef D& DRef; // no class/struct specifier 

2

ฉันมีปัญหาเดียวกันไม่ต้องการยุ่งกับ typedefs หลายไฟล์ที่แตกต่างกันดังนั้นฉันแก้ไขมันด้วยการสืบทอด:

คือ:

class BurstBoss {

public:

    typedef std::pair<Ogre::ParticleSystem*, bool> ParticleSystem; // removed this with...

เคยทำ:

class ParticleSystem : public std::pair<Ogre::ParticleSystem*, bool>
{

public:

    ParticleSystem(Ogre::ParticleSystem* system, bool enabled) : std::pair<Ogre::ParticleSystem*, bool>(system, enabled) {
    };
};

ทำงานเหมือนจับใจ แน่นอนฉันต้องเปลี่ยนการอ้างอิงจาก

BurstBoss::ParticleSystem

เพื่อเพียง

ParticleSystem

1

ฉันแทนที่typedef( usingเป็นแบบเฉพาะ) ด้วยการสืบทอดและตัวสร้างการสืบทอด (?)

เป็นต้นฉบับ

using CallStack = std::array<StackFrame, MAX_CALLSTACK_DEPTH>;

แทนที่

struct CallStack // Not a typedef to allow forward declaration.
  : public std::array<StackFrame, MAX_CALLSTACK_DEPTH>
{
  typedef std::array<StackFrame, MAX_CALLSTACK_DEPTH> Base;
  using Base::Base;
};

วิธีนี้ฉันสามารถส่งต่อประกาศCallStackด้วย:

class CallStack;

0

ตามที่ Bill Kotsias ตั้งข้อสังเกตไว้วิธีเดียวที่สมเหตุสมผลในการเก็บรายละเอียด typedef ของคะแนนของคุณเป็นส่วนตัวและส่งต่อประกาศว่าเป็นมรดก คุณสามารถทำได้ดีกว่าด้วย C ++ 11 พิจารณาสิ่งนี้:

// LibraryPublicHeader.h

class Implementation;

class Library
{
...
private:
    Implementation* impl;
};
// LibraryPrivateImplementation.cpp

// This annoyingly does not work:
//
//     typedef std::shared_ptr<Foo> Implementation;

// However this does, and is almost as good.
class Implementation : public std::shared_ptr<Foo>
{
public:
    // C++11 allows us to easily copy all the constructors.
    using shared_ptr::shared_ptr;
};

0

เช่นเดียวกับ @BillKotsias ฉันใช้การสืบทอดและมันได้ผลกับฉัน

ฉันเปลี่ยนระเบียบนี้ (ซึ่งจำเป็นต้องมีส่วนหัวบูสต์ทั้งหมดในการประกาศ * .h)

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>

typedef boost::accumulators::accumulator_set<float,
 boost::accumulators::features<
  boost::accumulators::tag::median,
  boost::accumulators::tag::mean,
  boost::accumulators::tag::min,
  boost::accumulators::tag::max
 >> VanillaAccumulator_t ;
std::unique_ptr<VanillaAccumulator_t> acc;

ลงในประกาศนี้ (* .h)

class VanillaAccumulator;
std::unique_ptr<VanillaAccumulator> acc;

และการนำไปใช้ (* .cpp) คือ

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>

class VanillaAccumulator : public
  boost::accumulators::accumulator_set<float,
    boost::accumulators::features<
      boost::accumulators::tag::median,
      boost::accumulators::tag::mean,
      boost::accumulators::tag::min,
      boost::accumulators::tag::max
>>
{
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.