名前空間について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Yakiniku
記事: 17
登録日時: 4ヶ月前
住所: 神奈川県

名前空間について

#1

投稿記事 by Yakiniku » 3ヶ月前

いつもお世話になっております。
私の知識不足のため、以下のページのⅣ. GETプログラムにおいて3つ記述方法について理解できないことがあります。

http://kagasu.hatenablog.com/entry/2017/10/07/190551

☆1つ目の質問
上記のサイトのプログラムにおいて、

pplx::task<void> Get()

という文があるのですが、
これは、
https://msdn.microsoft.com/ja-jp/library/jj987780.aspx
の説明を見ますと、
名前空間::型 関数
のような並びになっているのでしょうか?
それとも、
名前空間::クラス名<型> 関数
という並びでしょうか?

普通はクラス内関数の定義ですと

int ClassName::hoge()
のように
型 クラス名::関数名
という並びになるものではないのでしょうか?
それとも、名前空間内の関数においては、クラス名の後に<void>を付ければ関数をvoid型で宣言するようになる決まりでもあるのですか?

☆2つ目の質問
pplx::task<void> Get()
{
return pplx::create_task([]
{}
~).then

となっていますが、
関数([])というように、引数”[]”が渡されているように見えるのですが、これは一体なんなのでしょうか?
変数を渡さなくて良いのですか?

☆3つ目の質問
pplx::task<void> Get()
関数内の、
.then関数の後に";"がなく"{~}"
となっていますが、関数の後にセミコロンがいらない書き方が存在するのでしょうか?

ーーーーーーーーーーーーー
以上が質問になります。
よろしくお願いします。

inemaru
記事: 108
登録日時: 1年前

Re: 名前空間について

#2

投稿記事 by inemaru » 3ヶ月前

Yakiniku さんが書きました: ☆1つ目の質問
名前空間::型 関数
のような並びになっているのでしょうか?
かなり雑に説明すると、task<void> を型だと考えれば良いです。
簡略化すると下記のような構成になっています。

コード:

namespace pplx
{
    template <class Ty>
    class task {/* 略 */};
    
	template<>
	class task<void> {/* 略 */};
}

pplx::task<void> Get() {
	pplx::task<void> inst;
	return inst;
}
Yakiniku さんが書きました: ☆2つ目の質問
関数([])というように、引数”[]”が渡されているように見えるのですが、これは一体なんなのでしょうか?
変数を渡さなくて良いのですか?
「ラムダ式」で調べてみてください。
引数として渡しているのは、関数と考えて良いです。

コード:

// 最小のラムダ式
[](){};
Yakiniku さんが書きました: ☆3つ目の質問
pplx::task<void> Get()
関数内の、
.then関数の後に";"がなく"{~}"
となっていますが、関数の後にセミコロンがいらない書き方が存在するのでしょうか?
2つ目の質問と同じく、
「ラムダ式」について確認してください。

コード:

#include <iostream>
using namespace std;

template <class Func>
void Proc(Func f) {
    f();
}

int main(void){
    Proc([](){
        cout << "実行されました。" << endl;
    });
    return 0;
}

YuO
記事: 909
登録日時: 7年前
住所: 東京都世田谷区

Re: 名前空間について

#3

投稿記事 by YuO » 3ヶ月前

inemaruさんの投稿に追加で。
Yakiniku さんが書きました:pplx::task<void> Get()

という文があるのですが、
これは、
https://msdn.microsoft.com/ja-jp/library/jj987780.aspx
の説明を見ますと、
名前空間::型 関数
のような並びになっているのでしょうか?
それとも、
名前空間::クラス名<型> 関数
という並びでしょうか?
(戻り値の)型 関数名 開き括弧 引数リスト 閉じ括弧
の並びです。
pptx::task<void>が戻り値の型,Getが関数名,引数リストは省略されています。
pptx::task<void>はpptxが名前空間,taskが名前空間内でのクラステンプレートの名前,voidはテンプレートの実引数です。

詳しくはテンプレートについて調べてみてください。

かずま

Re: 名前空間について

#4

投稿記事 by かずま » 3ヶ月前

Yakiniku さんが書きました: ☆1つ目の質問
上記のサイトのプログラムにおいて、

pplx::task<void> Get()

という文があるのですが、
これは文ではありません。

int Get() { return 3; } という関数定義があった時、
int Get() という文がある、とは言いませんよね。
文は、return 3; だけです。

コード:

宣言は、「宣言指定子 宣言子 ;」という形をしています。
次の宣言で、宣言指定子は型指定子の int です。
 int i;    // i はオブジェクト指定子です。
 int a[8]; // a[8] は 配列宣言子です。
 int *p;   // *p はポインタ宣言子です。
 int f();  // f() は関数宣言子です。
i, a, p, f は識別子で、a, a, p はオブジェクト名、f は関数名です。
オブジェクトというのは、変数のことだと思ってください。

int は識別子ではありませんが、
typedef int I; という宣言があると、
I f(); という関数宣言では、I も f も識別子です。

識別子には、名前空間名やクラス名を前置できます。

namespace Hoge { typedef int Int; } // Int は名前空間 Hoge 内で宣言された。
class Fuga { Hoge::Int Get(); }     // Get はクラス Fuga 内で宣言された。

Hoge::Int Fuga::Get();              // 関数 Get の宣言
Hoge::Int Fuga::Get() { return 5; } // 関数 Get の定義

テンプレートクラスを宣言すると、そのクラスを参照するときに
テンプレート引数を < > で付加します。

template<typename T> class task { public: T* p; };

task<void> Get() {
    task<void> t;
	t.p = 0;
	return t;
}

かずま

Re: 名前空間について

#5

投稿記事 by かずま » 3ヶ月前

inemaru さんが書きました:

コード:

// 最小のラムダ式
[](){};
これは最小のラムダ式ではありません。

++p; があると、これは式ではなく、文です。
++p が式です。

同様に、セミコロンのつかない [](){} がラムダ式です。
次に、最小のラムダ式は []{} です。

inemaru
記事: 108
登録日時: 1年前

Re: 名前空間について

#6

投稿記事 by inemaru » 3ヶ月前

かずま さんが書きました: 同様に、セミコロンのつかない [](){} がラムダ式です。
次に、最小のラムダ式は []{} です。
ご指摘ありがとうございます。
確かに、最小のラムダ式は、

コード:

[]{}
です。

混乱させてしまって申し訳ありません。

かずま

Re: 名前空間について

#7

投稿記事 by かずま » 3ヶ月前

Yakiniku さんが書きました: 私の知識不足のため、以下のページのⅣ. GETプログラムにおいて3つ記述方法について理解できないことがあります。

http://kagasu.hatenablog.com/entry/2017/10/07/190551
そのページのプログラムを分かりやすく変形してみました。

コード:

pplx::task<void> Get()
{
    return pplx::create_task(
        [] {
            http_client client(L"https://jsonplaceholder.typicode.com/posts/1");
            return client.request(methods::GET);
        }
    ).then(
        [](http_response response) {
            if (response.status_code() == status_codes::OK) {
                return response.extract_json();
            }
        }
    ).then(
        [](json::value json) {
            std::wcout << json[L"title"].as_string() << std::endl;
        }
    );
}
ラムダ式は名前のない関数ですが、名前を付けてみましょう。

コード:

pplx::task<void> Get()
{
    auto f1 = [] {
        http_client client(L"https://jsonplaceholder.typicode.com/posts/1");
        return client.request(methods::GET);
    };
    auto f2 = [](http_response response) {
        if (response.status_code() == status_codes::OK) {
            return response.extract_json();
        }
    };
    auto f3 = [](json::value json) {
        std::wcout << json[L"title"].as_string() << std::endl;
    };
    return pplx::create_task(f1).then(f2).then(f3);
}
本当の関数にしてみましょう。

コード:

auto f1() {
    http_client client(L"https://jsonplaceholder.typicode.com/posts/1");
    return client.request(methods::GET);
}

auto f2(http_response response) {
    if (response.status_code() == status_codes::OK) {
        return response.extract_json();
    }
}

auto f3(json::value json) {
    std::wcout << json[L"title"].as_string() << std::endl;
}

pplx::task<void> Get()
{
    return pplx::create_task(f1).then(f2).then(f3);
}
ラムダ式の理解が深まりましたか?

かずま

Re: 名前空間について

#8

投稿記事 by かずま » 3ヶ月前

かずま さんが書きました:

コード:

宣言は、「宣言指定子 宣言子 ;」という形をしています。
次の宣言で、宣言指定子は型指定子の int です。
 int i;    // i はオブジェクト指定子です。
 int a[8]; // a[8] は 配列宣言子です。
 int *p;   // *p はポインタ宣言子です。
 int f();  // f() は関数宣言子です。
i, a, p, f は識別子で、a, a, p はオブジェクト名、f は関数名です。
オブジェクトというのは、変数のことだと思ってください。
ちょっと間違いを訂正。

 int i; // i はオブジェクト宣言子です。

 i, a, p はオブジェクト名

Yakiniku
記事: 17
登録日時: 4ヶ月前
住所: 神奈川県

Re: 名前空間について

#9

投稿記事 by Yakiniku » 3ヶ月前

inemaru 様。
ラムダ式という記述方法があったのですね。
JavaScriptでラムダ式があるということは知っていたのですが、まさかC++にもあるとは思いませんでした。
調べようにも、これがラムダ式ということ自体知らなかったので、理解・学習への取っ掛かりになりました。
ありがとうございます。

YuO様。
テンプレートは初めて知る概念です。
今まで自分は型を1つ1つ別の関数で書いていたので、このような便利な機能がC++にあるとは目から鱗でした。
明らかに便利ですので、すぐに習得させていただきます。

かずま様。
非常に分かりやすい解説です。
最後の書き比べにより、ラムダ式への理解が深まりました。
特に『return pplx::create_task』の変形部分は、ラムダ式とそうでないものの大きな違いのような気がします。
このソースを参考に学習していきたいと思います。
ありがとうございました。

zeek

Re: 名前空間について

#10

投稿記事 by zeek » 3ヶ月前

正確には
 int i; // i は宣言子(宣言子識別子)です。
 int a[8]; // a[8] は 宣言子(a は宣言子識別子、[8] は配列宣言子)です。
 int *p; // *p は宣言子(* はポインタ宣言子、p は宣言子識別子)です。
 int f(); // f() は宣言子(f は宣言子識別子、() は関数宣言子)です。
ですね。

かずま

Re: 名前空間について

#11

投稿記事 by かずま » 3ヶ月前

zeek さんが書きました:正確には
 int i; // i は宣言子(宣言子識別子)です。
 int a[8]; // a[8] は 宣言子(a は宣言子識別子、[8] は配列宣言子)です。
 int *p; // *p は宣言子(* はポインタ宣言子、p は宣言子識別子)です。
 int f(); // f() は宣言子(f は宣言子識別子、() は関数宣言子)です。
ですね。
いいえ。

[8] は配列宣言子ではありません。
* はポインタ宣言子ではありません。
() は関数宣言子ではありません。

規格書に従って説明してみますが、C および C++ の規格は、
更新されるたびに記述が複雑になっているので、
JIS X3010:2003(ISO/IEC 9899:1999) いわゆる C99 に基づき、
JIS X3014:2003(ISO/IEC 14882:2003) いわゆる C++03 で
補足していきます。

コード:

6.7 宣言
構文規則
        宣言:
                宣言指定子列  初期化宣言子並びopt;
        宣言指定子列:
                記憶域クラス指定子  宣言指定子列opt
                型指定子  宣言指定子列opt
                型修飾子  宣言指定子列opt
                関数指定子  宣言指定子列opt
        初期化宣言子並び:
                初期化宣言子
                初期化宣言子並び , 初期化宣言子
        初期化宣言子:
                宣言子
                宣言子  =  初期化子
制約  宣言では,少なくとも一つの宣言子 (関数の仮引数又は構造体若しくは
共用体のメンバ以外のもの),タグ又は列挙体のメンバを宣言しなければならない。
  識別子が無結合である場合,その識別子の宣言(宣言子又は型指定子の中の)が
同じ有効範囲及び同じ名前空間の中で,二つ以上あってはならない。
ただし,タグの宣言は除く(6.7.2.3 参照)。
  同じオブジェクト又は同じ関数についての同じ有効範囲の中のすべての宣言は,
適合する型を指定しなければならない。
int a[8]; という宣言は、「宣言指定子列 初期化宣言子並び ;」である。
int は、宣言指定子列であり、型指定子である。
a[8] は、初期化宣言子並びであり、初期化宣言子であり、宣言子である。

コード:

6.7.2  型指定子
構文規則
        型指定子:
                void
                char
                short
                int
                long
                (以下略)
const int a[3] = { 1, 2, 3 }, b[2] = { 4, 5 };

const は、型修飾子であり、宣言指定子であり、
int は、型指定子であり、宣言指定子であるから
const int は、宣言指定子列である。
a[3] は宣言子であり、{ 1, 2, 3 } は初期化子であるから
a[3] = { 1, 2, 3 } は、初期化宣言子である。
b[2] は宣言子であり、{ 4, 5 } は初期化子であるから
b[2] = { 4, 5 }; は、初期化宣言子である。
よって、a[3] = { 1, 2, 3 }, b[2] = { 4, 5 }; は、初期化宣言子並びである。

コード:

6.7.5  宣言子
構文規則
        宣言子:
                ポインタopt 直接宣言子
        直接宣言子:
                識別子
                ( 宣言子 )
                直接宣言子  [  型修飾子並びopt  代入式opt  ]
                直接宣言子  [  static  型修飾子並びopt  代入式  ]
                直接宣言子  [  型修飾子並び  static  代入式 ]
                直接宣言子  [  型修飾子並びopt  *  ]
                直接宣言子  (  仮引数型並び  )
                直接宣言子  (  識別子並びopt )
        ポインタ:
                *  型修飾子並びopt
                *  型修飾子並びopt  ポインタ
        型修飾子並び:
                型修飾子
                型修飾子並び  型修飾子
        仮引数型並び:
                仮引数並び
                仮引数並び  ,  ...
        仮引数並び:
                仮引数宣言
                仮引数並び  ,  仮引数宣言
        仮引数宣言:
                宣言指定子列    宣言子
                宣言指定子列    抽象宣言子opt
        識別子並び:
                識別子
                識別子並び  ,  識別子

意味規則  各宣言子は一つの識別子を宣言する。式の中にその宣言子と同じ形式の
オペランドが現れた場合,そのオペランドは,宣言指定子列が指示する有効範囲,
記憶域期間及び型をもつ関数又はオブジェクトを指し示す。
a[8] という宣言子は、「 直接宣言子 [ 代入式 ] 」である。
すなわち、「 直接宣言子 [ 型修飾子並びopt 代入式opt ] 」である。

a は、直接宣言子であり、識別子である。
8 は、代入式であり、条件式であり、論理OR式であり、論理AND式であり、
 排他OR式であり、AND式であり、等価式であり、関係式であり、シフト式であり、
 加減式であり、乗除式であり、キャスト式であり、単項式であり、後置式であり、
 一次式であり、定数である。

各宣言子は一つの識別子を宣言するという意味規則から、
[8] も * も () も宣言子ではありません。

コード:

6.7.5.1  ポインタ宣言子
意味規則
  宣言“T D1”において,D1 が形式
          *  型修飾子並びopt  D
をもつ場合,次のとおりとする。
ここには、構文規則がありませんが、それは「6.7.5 宣言子」の

コード:

        宣言子:
                ポインタopt 直接宣言子
        ポインタ:
                *  型修飾子並びopt
                *  型修飾子並びopt  ポインタ
に書かれています。

コード:

6.7.5.2  配列宣言子
制約  [ 及び ] の間には,省略可能な型修飾子及びキーワード static に加え,
式又は * を置くことができる。[ 及び ] が(配列の大きさを指定する)式を
囲む場合,その式の型は整数型でなければならない。式が定数式の場合,その値は
0 より大きくなければならない。要素の型が不完全型又は関数型であってはなら
ない。省略可能な型修飾子及びキーワード static は,配列型をもつ関数仮引数の
宣言中だけで使用できる。そして,一番外側の配列型派生だけで使用できる。
ここには、構文規則がありませんが、それは「6.7.5 宣言子」の

コード:

        直接宣言子:
                直接宣言子  [  型修飾子並びopt  代入式opt  ]
                直接宣言子  [  static  型修飾子並びopt  代入式  ]
                直接宣言子  [  型修飾子並び  static  代入式 ]
                直接宣言子  [  型修飾子並びopt  *  ]
に書かれています。

コード:

6.7.5.3  関数宣言子(関数原型を含む)
制約  関数宣言子は,関数型又は配列型を返却値の型として指定してはならない。
仮引数宣言に記憶域クラス指定子として,register 以外のものを指定しては
ならない。関数定義の一部でない関数宣言子における識別子並びは,空でなければ
ならない。関数定義の一部である関数宣言子の仮引数型並びにある仮引数は,
型調整後に不完全型をもってはならない。
ここには、構文規則がありませんが、それは「6.7.5 宣言子」の

コード:

        直接宣言子:
                直接宣言子  (  仮引数型並び  )
                直接宣言子  (  識別子並びopt )
に書かれています。

zeek

Re: 名前空間について

#12

投稿記事 by zeek » 3ヶ月前

失礼しました。今まで勘違いしていました。

C++ のスレッドですので C++ 規格から確認してみると
C++11 FDIS では
8 Declarators
The declarators specify the names of these entities and (optionally) modify
the type of the specifiers with operators such as * (pointer to) and () (function returning).
ということで、「宣言子は * () 等の演算子にて型を変更可能」としているため * () [] は演算子の
位置付けなんですね。
続けて
declarator:
ptr-declarator
noptr-declarator parameters-and-qualifiers trailing-return-type
ptr-declarator:
noptr-declarator
ptr-operator ptr-declarator
noptr-declarator:
declarator-id attribute-specifier-seqopt
noptr-declarator parameters-and-qualifiers
noptr-declarator [ expressionopt] attribute-specifier-seqopt
( ptr-declarator )
...
ptr-operator:
* attribute-specifier-seq(opt) cv-qualifier-seq(opt)
& attribute-specifier-seq(opt)
&& attribute-specifier-seq(opt)
nested-name-specifier * attribute-specifier-seq(opt) cv-qualifier-seq(opt)

上記(opt)は、optionally(右下の小さい opt)を意味する

ptr-operator として * & && があり、* は間接参照演算子ではなくポインタ演算子という
べきでしょうね。
整理すると
 int i; // i は宣言子(宣言子識別子)です。
 int a[8]; // a[8] は 配列宣言子(a は宣言子識別子、[] は配列演算子)です。
 int *p; // *p はポインタ宣言子(* はポインタ演算子、p は宣言子識別子)です。
 int f(); // f() は関数宣言子(f は宣言子識別子、() は関数演算子)です。
ということですね。
なお、
array-declarator と function-declarator と array-operator と function-operator という
単語はでてこないため 配列宣言子 関数宣言子 配列演算子 関数演算子 という言葉を使って
いいものかははっきりしません。

参考までに C規格として n1124(C99 next draft版)では
declarator:
pointeropt direct-declarator
pointer:
* type-qualifier-listopt
* type-qualifier-listopt pointer
となっていて C 規格ではポインタ演算子ではなく単にポインタでした。

返信

“C言語何でも質問掲示板” へ戻る