次のスニペットについて考えてみます。
void Foo()
{
// ...
}
void Bar()
{
return Foo();
}
より一般的なアプローチとは対照的に、C++で上記を使用する正当な理由は何ですか。
void Foo()
{
// ...
}
void Bar()
{
Foo();
// no more expressions -- i.e., implicit return here
}
あなたの例ではおそらく役に立たないでしょうが、テンプレートコードで処理するのが難しい状況がいくつかあり、void
このルールが時々それを助けることを期待しています。非常に工夫された例:
#include <iostream>
template <typename T>
T retval() {
return T();
}
template <>
void retval() {
return;
}
template <>
int retval() {
return 23;
}
template <typename T>
T do_something() {
std::cout << "doing something\n";
}
template <typename T>
T do_something_and_return() {
do_something<T>();
return retval<T>();
}
int main() {
std::cout << do_something_and_return<int>() << "\n";
std::cout << do_something_and_return<void*>() << "\n";
do_something_and_return<void>();
}
から戻るものがない場合にのみmain
対処する必要があることに注意してください。中間関数は汎用です。void
retval
do_something_and_return
もちろん、これはこれまでのところしか得られません-do_something_and_return
通常の場合、変数に格納retval
して戻る前にそれを使って何かをしたい場合は、まだ問題があります-に特化(または過負荷)する必要がありdo_something_and_return
ます空所。
これは、テンプレートで使用されない限り、目的を果たさないかなり役に立たない構造です。つまり、「void」の値を返すテンプレート関数を定義した場合です。
Foo()の戻り値が不明であるか、変更される可能性があるジェネリックコードで使用します。検討:
template<typename Foo, typename T> T Bar(Foo f) {
return f();
}
この場合、Barはvoidに対して有効ですが、リターンタイプが変更された場合にも有効です。ただし、単にfを呼び出すだけの場合、Tがvoidでない場合、このコードは壊れます。リターンf();を使用します。構文は、Foo()の戻り値が存在する場合はその値の保持を保証し、void()を許可します。
さらに、明示的に戻ることは、入るのに良い習慣です。
テンプレート:
template <typename T, typename R>
R some_kind_of_wrapper(R (*func)(T), T t)
{
/* Do something interesting to t */
return func(t);
}
int func1(int i) { /* ... */ return i; }
void func2(const std::string& str) { /* ... */ }
int main()
{
int i = some_kind_of_wrapper(&func1, 42);
some_kind_of_wrapper(&func2, "Hello, World!");
return 0;
}
voidを返すことができないとreturn func(t)
、ラップするように要求されたときにテンプレート内のが機能しませんでしたfunc2
。
私が考えることができる唯一の理由はreturn Foo();
、スイッチにステートメントの長いリストがあり、それをよりコンパクトにしたい場合です。
その理由は、math.hが常に返すようにメモリを返すためです。math.hにはvoid引数も空の引数もありません。あなたが記憶を必要とする多くの実際的な状況があります。
Foo()
最初に値を返したが、後でに変更されvoid
、それを更新した人があまり明確に考えていなかった場合である可能性があります。