BjarneはこのADLの例について間違っていますか、それともコンパイラのバグがありますか?


81

私はC ++プログラミング言語第4版Bjarne Stroustrupによる)について読んでいます。引用は次のとおりです(26.3.6、過度に攻撃的なADL):

引数に依存するルックアップ(ADLと呼ばれることが多い)は、冗長性を回避するのに非常に役立ちます(14.2.4)。例えば:

#include <iostream>

int main()
{
    std::cout << "Hello, world" << endl; // OK because of ADL
}

引数に依存するルックアップendlがないと、マニピュレータは見つかりません。そのまま、コンパイラは、への最初の引数<<がでostream定義されていることに気付きstdます。したがって、それは探しendlstd、それ(内を検索します<iostream>)。

そして、コンパイラー(C ++ 11モード)によって生成された結果は次のとおりです。

prog.cpp: In function ‘int main()’:
prog.cpp:4:36: error: ‘endl’ was not declared in this scope
 std::cout << "Hello, world" << endl;
                                ^

これはコンパイラまたは本のバグです。規格は何と言っていますか?

更新:

少し明確にする必要があります。正しい答えはを使用することstd::endlです。質問は本の中のテキストについてでした。ラクランイーストンはすでにそれだけでタイプミスではない、と述べました。段落全体が(おそらく)間違っています。その本が他の(あまり知られていない)著者によるものであれば、私はこの種の誤りを受け入れることができますが、それがビャーネによって書かれたので、私は疑っていました(そして今でもそうです)。


12
std::endlバグなし
aaronman 2013

3
私の経験では、本はバグやタイプミスで有名です。うまくいけば、良い本ではマイナー/明白なだけです。
ニール・カーク

31
@aaronmanOPは明らかにそれを認識しています。引用から、Bjarne (C ++の作成者)std::、ADLのため、この場合は必要ないと主張しているようです。しかし、これはコンパイルされないので、問題です。
BlueRaja-Danny Pflughoeft 2013

6
はい、要点は、本が明らかに間違ったことを言っているということです。これはタイプミスではなく、実際には真実ではないことを説明するために段落全体が書かれています。それは本のバグです。
DanielKO 2013

7
@maverikそれ本の誤りです。私はこの問題を数分前に彼に報告しました。彼の答えをお知らせします。
アリ

回答:


83

コンパイラのバグではありません。ADLは、引数ではなく関数を検索するために使用されますoperator<<ここでADLを介して、パラメーターstd::coutと(どうあるべきか)を調べて見つけた関数std::endlです。


2
実際には、振り返ってみると、これは事実悪用してコードが有効にする方法鼓舞std::endl(紛らわしいと)実際の問題としてあるの機能:endl(std::cout << "Hello, world"); // OK because of ADL
alfC

49

タイプミスだと言っている人にとっては、そうではありません。Bjarneがミスを犯したか、コンパイラーがミスを犯しました。OPによって投稿された段落の後の段落は

引数に依存するルックアップがないと、endlマニピュレータは見つかりません。そのまま、コンパイラは<<への最初の引数がstdで定義されたostreamであることを認識します。したがって、stdでendlを検索し、(で<iostream>)見つけます。


18
本で実際に読んだのはあなただけのようです。これは、言語ルールの大幅な変更であり、現在のすべてのC ++コンパイラが非標準(C ++ 11の場合)になるか、Stroustrup氏からの明白なエラー(タイプミスだけでなく)のいずれかです。改訂版を入手するのにさらに2か月待ちたかったのです。彼はあごひげを生やしたほうがいい。
DanielKO 2013

ちなみに、マークアップは引用符の最後のビットを食べたので、おそらくバッククォート「(in <iostream>)」を使用したいと思うでしょう。
DanielKO 2013

20

他の人がすでに指摘しているように、それは本のタイプミスです。しかし、本の意味するところは、 私たちが書かなければならないということです

std::operator<<(std::cout, "Hello, world").operator<<(std::endl);

ADLなし。それが、Bjarneが冗長性によって意味したことです。


私は正直に立っています。以下のようラクランイーストンは指摘し、それはタイプミスではなく、ある間違いブックインチ 私はこの本にアクセスできないので、その段落を読んで自分で理解することができませんでした。私はこの間違いをBjarneに報告し、彼がそれを修正できるようにしました。


おかしい。同じ例がウィキペディアにあり、

std::endlこれは関数ですが、operator<<std::endlは関数ポインタであり、関数呼び出しではありません)の引数として使用されるため、完全な修飾が必要であることに注意してください。

間違いなく、それは本の間違いです。それにもかかわらず、この例 std::operator<<(std::cout, "Hello, world").operator<<(std::endl);は、ADLが冗長性の削減にどのように役立つかを示しています。


私の間違い指摘してくれたgx_に感謝します。


それはタイプミス以上のものでした、彼は何か(ルックアップがどのようにstd::operator<<起こるか)を考え、間違った情報で段落全体を書きました。ADLのルールが変更され、コンパイラが壊れていると本当に信じ込ませます。
DanielKO 2013

実際、この本にはかなりの数のタイプミスがあるようです。例:17.2.5
AndersK

@DanielKO私は正直に立っています。答えを修正しました、ありがとう。私はこの本にアクセスできないので、タイプミスだと思いました。いずれにせよ、ADLは冗長性を減らすのに役立ちます。私が提供したコードはその一例です。とにかく、教えてくれてありがとう。
アリ

実際に私たちが書かなければならないのはstd::operator<<(std::cout, "Hello, world").operator<<(std::endl);非メンバーoperator<<メンバーをoperator<<参照)
gx_ 2013年

10

ヒントは、「引数依存のルックアップ」という名前にあります。

引数に応じて機能する、修飾されていない関数名の検索です。

これは、ルックアップとは何の関係も持っていないだために引数を。

ビャーネのミスポーク。


8

私は本を​​持っていませんが、これは本の誤りのようです。名前空間修飾子が欠落しているという事実は、ADLとは何の関係もありません。する必要がありますstd::endl


1
同意する。しかし、これはかなり奇妙な声明です(私は本の中のものを意味します)。ビャーネがそれについて知ってくれることを願っています。
maverik 2013

@maverik多分彼はすでにそうしている、誰かがすでにこれを報告したことには驚かないだろう。そうでない場合は、次のことができます:)
Borgleader 2013

@maverikそれは本当にタイプミスです、私は他の誰かがすでにそれに気づいていると思います
aaronman 2013

2
うん、本当に、私はすべてのステートメントを誤解しました(とstd::cout)彼はoperator<<ではなく、を検索することについて話していましたendl
maverik 2013

4

はい、それはエラーです-例は形式が正しくなく、コンパイルすべきではありません。ADLは、関数呼び出し式を導入する非修飾関数名に適用されます。 endlルックアップを試みるid式std::endlです。 endlは関数呼び出し式を導入しないため、引数依存のルックアップは使用されず、修飾されていないルックアップのみが使用されるためstd::endl、意図したとおりに検索されません。

より単純で正しい例は次のとおりです。

#include <vector>

int main()
{
    std::vector<int> x, y;
    swap(x,y); // calls std::swap due to ADL
}

要約するf(x,y,z)と、修飾されていないID (eg )を持つ関数呼び出し(eg )fが検索される前に、まず関数(eg x,y,z)のパラメーターが分析されてタイプが判別されます。関連する名前空間のリストは、タイプに基づいて形成されます(たとえば、タイプの定義を囲む名前空間は関連する名前空間です)。次に、これらの名前空間で関数がさらに検索されます。

Bjarneの例の意図は、std::operator<<関数のADLを誇示することであり、ではありませんstd::endl。これには、オーバーロードされた演算子が実際には関数呼び出し式であり、x << yoperator<<(x,y)、を意味し、operator<<修飾されていない名前であるため、ADLが適用されることをさらに理解する必要があります。LHSのタイプがあるstd::ostreamので、std関連付けられた名前空間であるため、std::operator<<(ostream&, ...)発見されました。

修正された解説は次のようになります。

引数に依存するルックアップがないと<<std名前空間でオーバーロードされた演算子は見つかりません。そのまま、コンパイラは<<への最初の引数がstdで定義されたostreamであることを認識します。したがって、<<stdで演算子を探し、(で<iostream>)見つけます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.