web-dev-qa-db-ja.com

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

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
100
user3111311

C++ 11では、type aliasに使用されるusingキーワードは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
using PF = void (*)(double);    // using plus C-style type
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

このイディオムの基本的な問題、およびこの提案の主な動機付けの事実は、イディオムによって、テンプレートパラメータが演context不可能なコンテキストで表示されることです。つまり、明示的にテンプレート引数を指定しないと、以下の関数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」という用語を特に避け、混乱を避けるために「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> >を使用します。そのように読めば、エイリアシングの新しい構文は合理的であるように思えます。

ここで重要な区別がなされていると思います、aliasesではなくtype s。同じドキュメントからの別の引用:

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

要約、usingの役割:

  • テンプレートのエイリアス(またはテンプレートのtypedef、前者が名前の方が好ましい)
  • 名前空間のエイリアス(つまり、namespace PO = boost::program_optionsおよびusing 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);
85
user1508519