1. ホーム
  2. c++

[解決済み] C++のusingキーワードはどのような理屈なのでしょうか?

2022-03-12 13:16:50

質問

C++のキーワード「"using"」はどのような理屈で使われているのでしょうか?

様々な場面で使用されており、私はそれを見つけようとしています。 これらには何か共通点があり、何か理由があるのでしょうか。 なぜこのようなキーワードが使われるのでしょうか?

using namespace std; // to import namespace in the current namespace
using T = int; // type alias
using SuperClass::X; // using super class methods in derived class

解決方法は?

C++11では using キーワードを使用した場合 type alias と同じです。 typedef .

7.1.3.2

typedef-nameは別名宣言で導入することもできる。その場合 using キーワードに続く識別子が typedef-nameになり、using キーワードに続く 識別子の後に続くオプションの属性指定子-seqは、その属性に属する をその型定義名と一致させる。と同じ意味合いを持つ。 はtypedef指定子によって導入される。特に,以下のものを定義しない。 は,新しい型であり,type-id に出現してはならない。

Bjarne Stroustrupが実例を示しています。

typedef void (*PFD)(double);    // C style typedef to make `PFD` a pointer to a function returning void and accepting double
using PF = void (*)(double);    // `using`-based equivalent of the typedef above
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP

C++11以前では using キーワードは、メンバ関数をスコープに取り込むことができます。C++11では、これをコンストラクタに適用できるようになりました(Bjarne Stroustrupの別の例)。

class Derived : public Base { 
public: 
    using Base::f;    // lift Base's f into Derived's scope -- works in C++98
    void f(char);     // provide a new f 
    void f(int);      // prefer this f to Base::f(int) 

    using Base::Base; // lift Base constructors Derived's scope -- C++11 only
    Derived(char);    // provide a new constructor 
    Derived(int);     // prefer this constructor to Base::Base(int) 
    // ...
}; 


Ben Voightは、新しいキーワードや新しい構文を導入しない根拠として、かなり適切な理由を提示しています。規格は、古いコードを壊すことをできるだけ避けたいのです。このため、提案文書には次のようなセクションがあります。 Impact on the Standard , Design decisions そして、それらが古いコードにどのような影響を及ぼす可能性があるかということです。ある提案が本当に良いアイデアに思えても、実装が難しすぎる、混乱しすぎる、古いコードと矛盾するなどの理由で、支持を得られない場合があるのです。


2003年の古い論文を紹介します。 n1449 . 根拠はテンプレートに関連しているようです。注意:PDFからコピーオーバーしているため、誤字脱字がある可能性があります。

<ブロッククオート

まず、おもちゃのような例で考えてみましょう。

template <typename T>
class MyAlloc {/*...*/};

template <typename T, class A>
class MyVector {/*...*/};

template <typename T>

struct Vec {
typedef MyVector<T, MyAlloc<T> > type;
};
Vec<int>::type p; // sample usage

このイディオムの根本的な問題点、そして、このイディオムを採用した主な動機は この提案の理由は、このイディオムによって、テンプレート・パラメータが が非依存的な文脈で現れる。つまり を明示的に指定することなく、以下の関数fooを呼び出すことができます。 の引数を指定します。

template <typename T> void foo (Vec<T>::type&);

というわけで、やや不格好な構文になってしまいました。むしろ、ネストされた ::type 次のようなものがいい。

template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage

特に "typedef template "という言葉を使わず、"typedef "と表記していることに注意。 using "と"="のペアを含む新しい構文を導入することで、この構文を避けることができます。 ここでは、型を定義しているわけではなく タイプID(すなわち型)を抽象化した同義語(すなわちエイリアス)。 式)のテンプレートパラメータを含んでいます。もしテンプレート・パラメータが が型式表現において演繹的な文脈で使用されている場合は、常に テンプレートエイリアスがテンプレートIDを形成するために使われるとき 対応するテンプレート・パラメータを推論することができる - これについては後述する。 の後に続きます。いずれにせよ、現在では、汎用関数 を操作する Vec<T> を推論可能なコンテキストで提供し、構文は も改善されました。例えば、fooを次のように書き換えることができる。

template <typename T> void foo (Vec<T>&);

を提案した主な理由の一つをここで強調しておく。 テンプレートエイリアスは、引数控除と foo(p) は成功する。


フォローアップ論文 n1489 の理由を説明します。 using を使用するのではなく typedef :

typedefというキーワードを(再)使用することが提案されている。 論文[4] - テンプレートエイリアスを導入するために。

template<class T> 
    typedef std::vector<T, MyAllocator<T> > Vec;

この記法は、すでに知られているキーワードを使うことができるという利点があります。 は、型の別名を導入します。しかし、この方法にはいくつかの デメリットは、特に、「negative」キーワードを使用することによる混乱です。 型名の別名を導入するコンテキストで、その別名が型名でない場合 は、型を指定するのではなく、テンプレートを指定します。 Vec のエイリアスではありません。 型と同じであり,型定義名と同じとみなしてはならない。型名 Vec は 家名 std::vector< [bullet] , MyAllocator< [bullet] > > - ここで、箇条書きはタイプ名のプレースホルダーである。その結果、私たちは typedef」構文は提案しない。一方,文

template<class T>
    using Vec = std::vector<T, MyAllocator<T> >;

は、次のように読み解くことができます。 Vec<T> として の同義語です。 std::vector<T, MyAllocator<T> > . このように読むと エイリアシングの新しい構文は、合理的だと思います。

重要なのは、ここでの区別だと思います。 エイリアス の代わりに タイプ s. もう一つ、同じ文書から引用します。

<ブロッククオート

alias-declarationは宣言であり、定義ではありません。エイリアス 宣言は、ある名前をエイリアスとして宣言的な領域に導入します。 は、宣言の右辺で指定された型のために使用されます。この この提案の核心は、型名エイリアスに関するものですが の表記を一般化して、別の綴りを提供することができます。 の名前空間エリシングやオーバーロードされた関数の名前付け(✁を参照)。 2.3で詳しく説明します)。[ 注:このセクションでは、その構文がどのように見えるか、そしてそれが提案の一部でない理由を論じています。 ] 文法プロダクション alias-declaration は、typedef 宣言や名前空間別名定義が許容されます。

まとめ:役割としては using :

  • テンプレートのエイリアス (またはテンプレートの型定義、名前的には前者が好ましい)
  • 名前空間エイリアス(つまり。 namespace PO = boost::program_optionsusing PO = ... に相当)
  • という文書があります。 A typedef declaration can be viewed as a special case of non-template alias-declaration . 美的な変化であり、この場合は同一と見なします。
  • スコープに入れる(例. namespace std グローバルスコープへ)、メンバ関数、コンストラクタの継承

それは できない に使える。

int i;
using r = i; // compile-error

そのかわり、こうしてください。

using r = decltype(i);

オーバーロードの集合に名前を付ける。

// bring cos into scope
using std::cos;

// invalid syntax
using std::cos(double);

// not allowed, instead use Bjarne Stroustrup function pointer alias example
using test = std::cos(double);