C ++の「using」キーワードの背後にあるロジックは何ですか?


145

C ++の「using」キーワードの背後にあるロジックは何ですか?

それはさまざまな状況で使用されており、それらすべてに共通点があるかどうかを探しています。「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

53
標準委員会は、C ++文法に新しいキーワードを導入することを嫌っています。
インターネットはcatz製で、13

4
@tehinternetsismadeofcatzそれが本当に論理なら、私が今行って自殺することを許しなさい。
user3111311

62
@ user3111311:新しい予約語を導入することの意味を理解していますか?つまり、これらを識別子名として使用していたすべての既存のコードは、突然コンパイルに失敗します。それは悪いことです。たとえば、のようなものが含まれているため、C ++としてコンパイルできないCコードがたくさんありますint class;。C ++コードが突然有効なC ++でなくなった場合、それはさらに悪化します。
Ben Voigt

7
@BenVoigt:使用int class;するCコードがC ++としてコンパイルされないという事実は、完全に悪いことではありません。これは、CコードがCとしてコンパイルされることを保証するために使用できます。CとC ++が2つの異なる言語であることを忘れるのは簡単です。実際には、有効なCと有効なC ++であるが、セマンティクスが異なるコードがあります。
キース・トンプソン

1
その点で、はと同じusingかそれ以上(またはそれ以上)ではありませんstatic。私見では、新しいキーワードを導入しないという点は非常に重要です。インターネットはcatzとBen Voigtで作られています。
Cassio Neri

回答:


114

C ++ 11では、usingに使用した場合のキーワードtype aliasはと同じですtypedef

7.1.3.2

typedef-nameは、エイリアス宣言によって導入することもできます。usingキーワードに続く識別子はtypedef-nameになり、識別子に続くオプションのattribute-specifier-seqはそのtypedef-nameに付随します。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 StandardDesign 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テンプレート」という用語は特に避け、混乱を避けるために「using」と「=」のペアを含む新しい構文を導入していることに注意してください。ここではタイプを定義していないため、テンプレートパラメータを含むtype-id(つまり、型式)の抽象化。テンプレートパラメーターが型式の推定可能なコンテキストで使用される場合、テンプレートエイリアスを使用してテンプレートIDを形成するときはいつでも、対応するテンプレートパラメーターの値を推定できます。これについては後ほど説明します。いずれの場合も、Vec<T>推測可能なコンテキストで動作する汎用関数を作成できるようになり、構文も改善されました。たとえば、fooを次のように書き換えることができます。

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

ここで、テンプレートエイリアスを提案する主な理由の1つは、引数の演繹とへの呼び出しfoo(p) が成功するようにしたことを強調します。


フォローアップペーパーn1489は、以下usingを使用する代わりに理由を説明していtypedefます。

テンプレートのエイリアスを導入するには、論文[4]で行われているように、キーワードtypedefを(再)使用することが推奨されています。

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

その表記法には、型エイリアスを導入するためにすでに知られているキーワードを使用するという利点があります。ただし、エイリアスがタイプではなくテンプレートを指定するコンテキストでタイプ名のエイリアスを導入することがわかっているキーワードを使用することの混乱など、いくつかの欠点も表示されます。Vecは型のエイリアスではないため、typedef-nameには使用しないでください。名前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 ではなくエイリアス es です。同じ文書からの別の引用:

エイリアス宣言は宣言であり、定義ではありません。別名宣言は、宣言の右側で指定された型の別名として、宣言型領域に名前を導入します。この提案の核心は型名のエイリアスに関係していますが、表記法は明らかに一般化して、名前空間のエイリアスの代替スペルまたはオーバーロードされた関数の名前のセットを提供できます(詳細については、✁2.3を参照してください)。[ 私のメモ:そのセクションでは、その構文がどのように見えるか、およびそれが提案の一部ではない理由について説明します。]文法生成のエイリアス宣言は、typedef宣言または名前空間エイリアス定義が受け入れられる場所であればどこでも受け入れられることに注意してください。

まとめusing

  • テンプレートのエイリアス(またはテンプレートのtypedefs、前者は名前ごとに推奨)
  • 名前空間のエイリアス(すなわち、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);

2
@ user3111311他にどんなキーワードを考えましたか?「自動」?"登録"?
Raymond Chen

2
using P = [](double)->void;AFAIK、無効なC ++ 11です。ただし、これはusing P = auto(double)->void;関数型(P*関数ポインターなど)を生成します。
dyp 2013

2
彼の名前はBjarne Stroustrupです;)(Stroustrupの2番目のrに注意してください)
dyp

1
@RaymondChen:実際にregisterその悪い音ではないだろう、である:register X as Y
MFH

1
残念ながらregister変数宣言を開始するので、これはすでに意味があります。タイプXのYと呼ばれるレジスタ変数宣言
Raymond Chenは
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.