最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Why does the compiler accept a 'using' directive aliasing a template with an incomplete type? - Stack Over

programmeradmin10浏览0评论

I struggled with my question title... I hope it will be clearer with a snippet.

This is derived and simplified from Attorney-Client idiom.

File header_A.h

// forward declaration
struct B;

template <typename T>
class Tag {
    friend T;
    // tag can be constructed only by a T
    Tag() = default;
};

struct A {
    int val = 0;
    using tag = Tag<B>; // B is an incomplete type here
    // using type in a definition though
    // only a B object can use this function because only a B object can build a tag
    void Set(int i, const tag &) { val = i; }
};

File header_B.h

struct B {
    void Set(A& a, int i) { a.Set(i, Tag<B>{}); }
};

File main.cpp

#include "header_A.h"
#include "header_B.h"

int main() {
    A a;
    B b;
    b.Set(a,42);
}

All this compiles and links just fine: LIVE

But, is it actually valid? What are the standard rules that apply?

In particular, why can I write using tag = Tag<B>; and then use tag in a definition, while B is still unknown?

I suspect that it's because the actual type of B is never needed (as hinted there), but I'd like to understand what happens exactly.

I struggled with my question title... I hope it will be clearer with a snippet.

This is derived and simplified from Attorney-Client idiom.

File header_A.h

// forward declaration
struct B;

template <typename T>
class Tag {
    friend T;
    // tag can be constructed only by a T
    Tag() = default;
};

struct A {
    int val = 0;
    using tag = Tag<B>; // B is an incomplete type here
    // using type in a definition though
    // only a B object can use this function because only a B object can build a tag
    void Set(int i, const tag &) { val = i; }
};

File header_B.h

struct B {
    void Set(A& a, int i) { a.Set(i, Tag<B>{}); }
};

File main.cpp

#include "header_A.h"
#include "header_B.h"

int main() {
    A a;
    B b;
    b.Set(a,42);
}

All this compiles and links just fine: LIVE

But, is it actually valid? What are the standard rules that apply?

In particular, why can I write using tag = Tag<B>; and then use tag in a definition, while B is still unknown?

I suspect that it's because the actual type of B is never needed (as hinted there), but I'd like to understand what happens exactly.

Share Improve this question edited Mar 21 at 21:11 Remy Lebeau 601k36 gold badges507 silver badges851 bronze badges asked Mar 21 at 21:07 OerstedOersted 2,9586 silver badges29 bronze badges 2
  • I suspect it's also because it's a const reference, therefore no knowledge is needed about how a hypothetical instance would be copied/moved etc. – SoronelHaetir Commented Mar 21 at 21:32
  • It is the passkey idiom – Jarod42 Commented Mar 22 at 13:14
Add a comment  | 

1 Answer 1

Reset to default 1

using tag = Tag<B>; only requires a declaration for Tag and a declaration for B. Neither of the two have to be defined. Note that the identity of Tag<B> as a type does only depend on declarations, not on the definitions of Tag and B. Tag<B> is the same type, no matter how Tag and B are defined.

A definition of a class or is only required in special situations, e.g. when attempting to access a member or when doing overload resolution that depends on the contents of the class definition, etc. Basically whenever the contents of the class definition are necessary to decide the validity or behavior of a given code.

Similarly implicit instantiation of a class template specialization only happens when it is used in such a context.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论