Chuyển tiếp khai báo của một typedef trong C ++


235

Tại sao trình biên dịch không cho phép tôi chuyển tiếp khai báo một typedef?

Giả sử không thể, cách tốt nhất để giữ cho cây bao gồm của tôi nhỏ là gì?

Câu trả lời:


170

Bạn có thể làm typedef chuyển tiếp. Nhưng để làm

typedef A B;

trước tiên bạn phải chuyển tiếp tuyên bố A:

class A;

typedef A B;

11
Cuối cùng +1 vì trong khi về mặt kỹ thuật bạn không thể "chuyển tiếp typedef" (nghĩa là bạn không thể viết "typedef A;"), bạn gần như chắc chắn có thể thực hiện được những gì OP muốn thực hiện bằng cách sử dụng thủ thuật của bạn ở trên.
j_random_hacker

9
Nhưng hãy lưu ý, nếu typedef thay đổi, bạn cũng có thể thay đổi tất cả các khai báo chuyển tiếp, điều mà bạn có thể bỏ lỡ nếu typedef cũ và mới sử dụng các loại có cùng giao diện.
toán

50
Nói chung đây không phải là một giải pháp hữu ích. Ví dụ: nếu typedeftên một loại mẫu đa cấp phức tạp sử dụng khai báo chuyển tiếp theo cách này thì khá phức tạp và khó khăn. Chưa kể rằng nó có thể yêu cầu đi sâu vào chi tiết triển khai ẩn trong các đối số mẫu mặc định. Và giải pháp cuối cùng là một mã dài và không thể đọc được (đặc biệt là khi các loại đến từ nhiều không gian tên khác nhau) rất dễ thay đổi trong loại ban đầu.
Adam Badura

3
Ngoài ra, điều này cho thấy "chi tiết thực hiện" (ngay cả khi không đầy đủ nhưng vẫn ...) trong khi ý tưởng đằng sau tuyên bố chuyển tiếp là để ẩn chúng.
Adam Badura

3
@windfinder: Nó hiện: template <class T> class A; typedef A <C> B;
milianw

47

Đối với những người như tôi, những người đang muốn chuyển tiếp khai báo một cấu trúc kiểu C được xác định bằng cách sử dụng typedef, trong một số mã c ++, tôi đã tìm thấy một giải pháp như sau ...

// 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 Vấn đề với giải pháp này là tên giả _bah không được coi là một phần của API công khai. Xem FILE delcare phía trước.
dùng877329

23

Để "fwd khai báo một typedef", bạn cần fwd khai báo một lớp hoặc một cấu trúc và sau đó bạn có thể gõ kiểu khai báo typedef. Nhiều typedefs giống nhau được chấp nhận bởi trình biên dịch.

dạng dài:

class MyClass;
typedef MyClass myclass_t;

hình thức ngắn:

typedef class MyClass myclass_t;

Điều này khác với câu hỏi được bình chọn nhiều nhất như thế nào? stackoverflow.com/a/804956/931303
Jorge Leitao

1
@ JorgeLeitão bạn không thấy nó khác nhau như thế nào? Nó không chỉ ra cách làm điều đó trong một dòng.
Pavel P

17

Trong C ++ (nhưng không đơn giản là C), việc nhập loại hai lần là hoàn toàn hợp pháp, miễn là cả hai định nghĩa hoàn toàn giống nhau:

// 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
Bảo trì Không. Loại điều này sẽ cắn bạn trong keister sớm hay muộn.
Đánh dấu Storer

3
@MarkStorer, ít nhất trình biên dịch sẽ bắt được bất kỳ sự khác biệt nào và tạo ra lỗi. Tôi đã xác minh điều này với Visual C ++.
Alan

Đẹp, nhưng làm thế nào để bạn xác định Acác trường theo cách này vì Atrống rỗng theo định nghĩa?
Patrizio Bertoni

10

Bởi vì để khai báo một loại, kích thước của nó cần phải được biết. Bạn có thể chuyển tiếp khai báo một con trỏ tới kiểu hoặc gõ con trỏ vào kiểu.

Nếu bạn thực sự muốn, bạn có thể sử dụng thành ngữ pimpl để giữ cho bao gồm xuống. Nhưng nếu bạn muốn sử dụng một loại, thay vì một con trỏ, trình biên dịch phải biết kích thước của nó.

Chỉnh sửa: j_random_hacker bổ sung một phẩm chất quan trọng cho câu trả lời này, về cơ bản là kích thước cần phải biết để sử dụng loại, nhưng có thể thực hiện khai báo chuyển tiếp nếu chúng ta chỉ cần biết loại tồn tại , để tạo con trỏ hoặc tham chiếu đến kiểu. Vì OP không hiển thị mã, nhưng phàn nàn rằng nó sẽ không biên dịch, tôi giả định (có lẽ chính xác) rằng OP đang cố gắng sử dụng loại, không chỉ đề cập đến nó.


35
Vâng, khai báo về phía trước của các loại lớp khai báo các loại này mà không có kiến ​​thức về kích thước của chúng. Ngoài ra, ngoài việc có thể xác định các con trỏ và tham chiếu đến các loại không hoàn chỉnh như vậy, các hàm có thể được khai báo (nhưng không được xác định) lấy tham số và / hoặc trả về giá trị của các loại đó.
j_random_hacker

3
Xin lỗi tôi không nghĩ rằng đó là một giả định tốt. Câu trả lời này là bên cạnh điểm. Đây là rất nhiều trường hợp của typedef một tuyên bố chuyển tiếp.
Cookie

6

Chỉ có thể sử dụng khai báo chuyển tiếp thay vì đầy đủ #includekhi bạn không có ý định sử dụng chính loại đó (trong phạm vi của tệp này) mà là một con trỏ hoặc tham chiếu đến nó.

Để sử dụng chính kiểu đó, trình biên dịch phải biết kích thước của nó - do đó phải xem khai báo đầy đủ của nó - do đó cần phải có đầy đủ #include.

Tuy nhiên, kích thước của một con trỏ hoặc tham chiếu được trình biên dịch biết, bất kể kích thước của con trỏ, vì vậy một khai báo chuyển tiếp là đủ - nó khai báo một tên định danh kiểu.

Thật thú vị, khi sử dụng con trỏ hoặc tham chiếu đến classhoặc structcác loại, trình biên dịch có thể xử lý các loại không hoàn chỉnh, tiết kiệm cho bạn nhu cầu chuyển tiếp khai báo các loại 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

Tôi có cùng một vấn đề, không muốn gây rối với nhiều typedefs trong các tệp khác nhau, vì vậy tôi đã giải quyết vấn đề đó bằng sự kế thừa:

là:

class BurstBoss {

public:

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

đã làm:

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

public:

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

Làm việc như người ở. Tất nhiên, tôi đã phải thay đổi bất kỳ tài liệu tham khảo nào từ

BurstBoss::ParticleSystem

chỉ đơn giản là

ParticleSystem

1

Tôi đã thay thế typedef( usingđể được cụ thể) bằng thừa kế và kế thừa hàm tạo (?).

Nguyên

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

Đã thay thế

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

Bằng cách này, tôi đã có thể chuyển tiếp tuyên bố CallStackvới:

class CallStack;

0

Như Bill Kotsias đã lưu ý, cách hợp lý duy nhất để giữ các chi tiết typedef của điểm của bạn ở chế độ riêng tư và chuyển tiếp tuyên bố chúng là với sự kế thừa. Bạn có thể làm điều đó đẹp hơn một chút với C ++ 11. Xem xét điều này:

// 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

Giống như @BillKotsias, tôi đã sử dụng tính kế thừa và nó hoạt động với tôi.

Tôi đã thay đổi mớ hỗn độn này (yêu cầu tất cả các tiêu đề tăng trong tuyên bố của tôi * .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;

vào tuyên bố này (* .h)

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

và việc thực hiện (* .cpp) là

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