0

プログラム 1:

#include <iostream>

std::string Hello(void){return "Hello";}
std::string World(void){return "world!";}

int main(){

    std::cout << Hello() << " " << World() << std::endl;
}

関数 Hello() および World() は、グローバル名前空間に存在します。
これは、宣言の後に続く他の関数から呼び出すことができることを意味します (または、この場合、定義を提供し、最初に宣言する必要はありませんでした)。

これには問題があります。プロジェクトが大きくなるにつれて、グローバル名前空間を多くの関数で埋めるヘッダー ファイルが増えるため、関数シグネチャの競合が発生したり、さらに悪いことに、呼び出されるだけのはずの間違った関数を誤って呼び出したりするリスクがあるためです。別の機能のサブタスクとして。

私は、タスクを機能的にサブタスクに分解するパラダイムに従おうとしているため、特定の関数が別の特定の関数の範囲外で呼び出されることは意味がありません。

ここに回避策があります。インデントの深さのためにコードが読めなくなることは別として、パフォーマンスまたは実装上の落とし穴があるかどうかを知りたいです。現時点ではラムダ関数はちょっとした魔法のようなものなので、予期せぬ危険性に興味があります。

プログラム 2:

#include <iostream>

int main(){

    auto Hello = [](void) -> std::string{return "Hello";};
    auto World = [](void) -> std::string{return "world!";};

    std::cout << Hello() << " " << World() << std::endl;
}

Hello() と World() は main() 内にカプセル化されており、main() のスコープ外から呼び出すことはできません。
違うのはそれだけ?

4

3 に答える 3

1

目的を達成するための C++ で推奨されるメカニズムは、モジュールに対して (関数、型、オブジェクト) をプライベート (キーワードとは異なり、アクセシビリティの意味ではありません) にして、別のモジュールで名前を付けることができないようにすることです。これにより、衝突が完全に防止されます。

たとえば、ヘッダー ファイルで構成されるモジュールを考えてみましょう。

// List dependencies
#include <string>

// It's customary to put things in a namespace
// Note that namespaces are commonly cross-module

namespace ns {

// Declarations visible to all
std::string hello_world();

} // ns

そしてそのような実装:

// include header listed above
#include <header.hpp>

// This contains declarations visible to this module only
namespace {

std::string hello_part();

// This is both a declaration and a definition
std::string world_part()
{ return "world!"; }

} // namespace

// Definition of function declared in header
std::string ns::hello_world()
{
    // Implemention defers to private parts that are visible here
    return hello_part() + " " + world_part();
}

// Definition of private function
// We need to reopen the unnamed namespace for that

namespace {

std::string hello_part()
{ return "Hello"; }

} // namespace

これで、別のモジュールが別のモジュールをhello_part独自に宣言できるようになりました。これは、同じ宣言を持つ関数である必要はなく、まったく関数である必要もありません。競合は発生しません。2 つの異なるエンティティが存在します。これは、forステートメントが独自のint i;変数を持つことができ、他に干渉することがないのと同じiです。

hello_partあなたの質問の他の部分については、world_part上記がプレーンな古い関数として実装されているか、関数オブジェクトとして実装されているかは大した問題ではありません(ラムダ式などを介して)。ただし、何も閉じないため、あまり閉じていないことに注意してください。C++ ラムダ式では、ローカル変数 (ローカル キャプチャとも呼ばれます) を含め、あらゆるものを閉じることができますが、型システムは、特定の状況でわずかなペナルティを支払うことなく、上向きの funarg 問題を許容できるほど十分に表現力がありません。ただし、C++の信条に忠実に、これは背後で発生することはありません。たとえば、そのペナルティを詳しく説明する必要があります。std::function. そのような場合、私はラムダ式に反対します。より良い代替手段があると思うからです (ここでは詳しく説明しません)。

ただし、一般に、ラムダ式を使用するかどうかを決定することは、プログラムのモジュール性に影響を与えない (またすべきではない) 考慮事項です。

より長く詳細な回答は、プログラムをモジュール化するために使用できるスコープと可視性、宣言と実装、名前空間、モジュール、およびアクセシビリティのさまざまな役割を説明します。それらを混同しないことが最善です。私の例では、アクセシビリティ以外のすべてのものを使用してニーズを達成していますが、これが唯一の方法ではありません。

于 2012-12-20T00:44:56.653 に答える
0

私はそれをしません。あなたの場合、最終的には内部のラムダの定義を可能にする巨大な関数を作成することになり、本体の内容を維持するのがはるかに難しい関数になります。

C ++ 11とラムダの前には非常に大規模なプロジェクトがあり、衝突のリスクは、すでに述べたDeadMGのような名前空間だけでなく、クラスやオブジェクト指向設計、その他の形式のカプセル化(ローカル関数を次のように定義する)など、さまざまな方法で管理されてきました。static実装ファイルの名前空間レベルで、内部リンケージを強制し、他の変換ユニットとの競合を回避します。その上で、識別子に意味のある名前を慎重に選択する場合は、99%の衝突を回避する必要があります。

あなたが本当に大規模なプロジェクトに取り組むつもりなら、ジョンのラコス大規模C++ソフトウェアデザインを読むことを検討してください

于 2012-12-20T00:27:37.313 に答える
0

まず、「名前空間」と呼ばれます。第二に、そのようなラムダにはマイナス面はありません。実際、それらには利点があります。内部でローカル変数を使用できるようになったため、より多くのロジックを再利用できるからです。

于 2012-12-20T00:04:31.920 に答える