C++わからない

id:yshl:20130623:1371964229 について

typeid してみた

using namespace std;
#include<iostream>
#include<typeinfo>

typedef struct {
    int x, y;
} Foo1;

typedef const struct {
    int x, y;
} Foo2;

typedef struct Foo3_ {
    int x, y;
} Foo3;

typedef const struct Foo4_ {
    int x, y;
} Foo4;

int main()
{
    cout<<"typeid(Foo1): "<<typeid(Foo1).name()<<endl;             // => 4Foo1
    cout<<"typeid(const Foo1): "<<typeid(const Foo1).name()<<endl; // => 4Foo1

    cout<<"typeid(Foo2): "<<typeid(Foo2).name()<<endl; // => 4._71 とか 3$_0 とか

    cout<<"typeid(Foo3_): "<<typeid(Foo3_).name()<<endl;           // => 5Foo3_
    cout<<"typeid(Foo3): "<<typeid(Foo3).name()<<endl;             // => 5Foo3_
    cout<<"typeid(const Foo3): "<<typeid(const Foo3).name()<<endl; // => 5Foo3_

    cout<<"typeid(Foo4_): "<<typeid(Foo4_).name()<<endl; // => 5Foo4_
    cout<<"typeid(Foo4): "<<typeid(Foo4).name()<<endl;   // => 5Foo4_
    return 0;
}
  1. struct Foo3_ { ... のように構造体タグ名が宣言されている場合はもちろん名前が付く。
    • わかる。
  2. struct { ... のように構造体タグ名が宣言されていない場合は名前が付かない。
    • わかる。
  3. typedef struct { ... } Foo1 のように無名の構造体に typedef すると名前が付く。
    • ふむ、typedef は型名を付けるものなのか。
  4. typedef const struct { ... } Foo2 のように const が付いていると、名前が付くのは const 付きの構造体で、const 無しの struct { ... } には名前が付かない。
    • ふむ、const 付きと const 無しは別の型なのか。
  5. typeid 演算子で型名が得られるが、const は修飾子なので関係ない。
    • ふむ、const 付きと const 無しは同じ型なのか……、え?
  6. Foo2 は const を除いた無名構造体 struct {...} と同様に型名は無い。typedef で付けたタグ名は型名にならない。
    • なんと、typedef は型名を付けるものではなかったのか……。

ええと……

  • typedef が型名を付けるもので const が付くと別の型になるなら typeid(Foo1) と typeid(const Foo1) は別になるべきでは?
  • typedef が型名を付けるもので const が付いても型は同じなら typedef const struct { ... } Foo2 で const 無しの struct { ... } にも型が付くべきでは?
  • typedef が型名を付けるものでは無いのなら typedef struct { ... } Foo1 で名前が付くのは変なのでは?
    • Foo1 のように名前が無い修飾子も無い型に対して typedef した場合は特別扱いみたいなルールなのかな?

追記
私は愚か者です。

  • 規格を読みなさい。
    • working draftでいい?
    • 3.5 Program and Linkage
    • 5.2.8 Type identification
    • 7.1.3 The typedef specifier
    • 7.1.6.1 The cv-qualifiers
    • 9.1 Class name

それはそれとしてリンクできるかどうか

foo.h

#ifndef FOO
#define FOO

typedef const struct {
    int x;
    int y;
} Foo;

extern Foo f;

#endif

foo.c

#include"foo.h"

Foo f={1,2};

main.c

#include<iostream>
#include"foo.h"

int main()
{
    std::cout << f.x << "," << f.y << std::endl;
    return 0;
}
g++ 3.4.6
$ g++-34 -Wall main.cc foo.cc

警告は出ずリンクも成功する。g++ 3.4.6 でも上の typeid(Foo2) は 4._47 と無名っぽいので、昔は型が無名でもリンクできたっぽい。

g++ 4.4.7
$ g++-44 -Wall main.cc foo.cc
In file included from main.cc:2:
foo.h:9: 警告: non-local variable 'const Foo f' uses anonymous type
foo.h:7: 警告: 'typedef const struct<anonymous> Foo' does not refer to the unqualified type, so it is not used for linkage
In file included from foo.cc:1:
foo.h:9: 警告: non-local variable 'const Foo f' uses anonymous type
foo.h:7: 警告: 'typedef const struct<anonymous> Foo' does not refer to the unqualified type, so it is not used for linkage

リンクできないみたいな警告は出る割にリンクできてしまう。よくわからない。

g++ 4.6.5

$ g++ -Wall main.cc foo.cc
In file included from main.cc:2:0:
foo.h:9:12: 警告: anonymous type with no linkage used to declare variable 'Foo f' with linkage [デフォルトで有効]
foo.h:7:3: 警告: 'typedef const struct<無名> Foo' does not refer to the unqualified type, so it is not used for linkage [デフォルトで有効]
In file included from foo.cc:1:0:
foo.h:9:12: 警告: anonymous type with no linkage used to declare variable 'Foo f' with linkage [デフォルトで有効]
foo.h:7:3: 警告: 'typedef const struct<無名> Foo' does not refer to the unqualified type, so it is not used for linkage [デフォルトで有効]
/tmp/ccSziGao.o: In function `main':
main.cc:(.text+0xb): undefined reference to `f'
main.cc:(.text+0x11): undefined reference to `f'
collect2: ld はステータス 1 で終了しました

「Foo f はリンクできない無名の型だけど、リンクできると宣言されてますよ」、「Fooはリンクに使えませんよ」という警告が出て、リンクはできない。

clang++ 3.2

$ clang++ -Weverything main.cc foo.cc
In file included from main.cc:2:
./foo.h:9:12: warning: variable 'f' has internal linkage but is not defined
extern Foo f;
           ^
main.cc:6:18: note: used here
    std::cout << f.x << "," << f.y << std::endl;
                 ^
1 warning generated.
/tmp/main-s0Ypfk.o: In function `main':
main.cc:(.text+0x1a): undefined reference to `f'
main.cc:(.text+0x36): undefined reference to `f'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

警告は出るのだが、extern と付けているのに f は internal linkage だとか言ってくるし、警告も「定義が無いよ」だし、よくわからない。

$ clang++ -Weverything foo.cc -c

とした場合は警告はない。リンクはできない。

まとめとしては、

今時の C++ では無名の型ではリンクできないけど、昔は違ったようだし、コンパイラの警告はよくわからない。