可変数の引数を受け入れる関数を作成するにはどうすればよいですか? これは可能ですか?
18 に答える
あなたはおそらくそうすべきではありません、そしてあなたはおそらくあなたがしたいことをより安全でより簡単な方法で行うことができます。技術的には、Cで可変数の引数を使用するには、stdarg.hをインクルードします。そこから、タイプと、それを操作する、、およびva_list
と呼ばれる3つの関数を取得します。va_start()
va_arg()
va_end()
#include<stdarg.h>
int maxof(int n_args, ...)
{
va_list ap;
va_start(ap, n_args);
int max = va_arg(ap, int);
for(int i = 2; i <= n_args; i++) {
int a = va_arg(ap, int);
if(a > max) max = a;
}
va_end(ap);
return max;
}
あなたが私に尋ねるなら、これは混乱です。見た目は悪く、安全ではなく、概念的に達成しようとしていることとは関係のない技術的な詳細が満載です。代わりに、オーバーロードまたは継承/ポリモーフィズム、ビルダーパターン(operator<<()
ストリームの場合のように)またはデフォルトの引数などを使用することを検討してください。これらはすべてより安全です。あなたがあなたの足を吹き飛ばす前にあなた。
C++11 では、可変引数テンプレートを実行する方法があり、これにより、可変引数関数を使用するための非常に洗練された型安全な方法が実現します。Bjarne 自身がC++11FAQで可変引数テンプレートを使用した printfの良い例を挙げています。
個人的には、これは非常に洗練されていると考えているため、コンパイラが C++11 可変引数テンプレートをサポートするまでは、C++ の可変引数関数を気にすることさえしません。
C スタイルの可変引数関数は、C++ でサポートされています。
ただし、ほとんどの C++ ライブラリは別のイディオムを使用します。たとえば、'c' printf
関数が変数の引数を取るのに対し、c++ cout
オブジェクトは<<
型の安全性と ADT に対応するオーバーロードを使用します (おそらく実装の単純さを犠牲にして)。
varargs またはオーバーロードとは別に、引数を std::vector または他のコンテナー (std::map など) に集約することを検討できます。このようなもの:
template <typename T> void f(std::vector<T> const&);
std::vector<int> my_args;
my_args.push_back(1);
my_args.push_back(2);
f(my_args);
このようにして、タイプ セーフが得られ、これらの可変個引数の論理的な意味が明らかになります。
確かに、このアプローチにはパフォーマンスの問題が発生する可能性がありますが、代償を払えないことが確実でない限り、心配する必要はありません。これは、C ++への一種の「Pythonic」アプローチです...
唯一の方法は、ここで説明されているように、C スタイルの変数引数を使用することです。これはタイプセーフではなく、エラーが発生しやすいため、推奨される方法ではないことに注意してください。
Cスタイルのvarargs(...
)を使用せずにこれを行う標準のC++の方法はありません。
もちろん、コンテキストに応じて可変数の引数のように「見える」デフォルトの引数があります。
void myfunc( int i = 0, int j = 1, int k = 2 );
// other code...
myfunc();
myfunc( 2 );
myfunc( 2, 1 );
myfunc( 2, 1, 0 );
4つの関数呼び出しはすべてmyfunc
、さまざまな数の引数を使用して呼び出します。何も指定されていない場合は、デフォルトの引数が使用されます。ただし、省略できるのは末尾の引数のみであることに注意してください。たとえば、を省略i
して与えるだけの方法はありませんj
。
オーバーロードまたはデフォルトのパラメーターが必要な可能性があります-デフォルトのパラメーターを使用して同じ関数を定義します。
void doStuff( int a, double termstator = 1.0, bool useFlag = true )
{
// stuff
}
void doStuff( double std_termstator )
{
// assume the user always wants '1' for the a param
return doStuff( 1, std_termstator );
}
これにより、4 つの異なる呼び出しのいずれかでメソッドを呼び出すことができます。
doStuff( 1 );
doStuff( 2, 2.5 );
doStuff( 1, 1.0, false );
doStuff( 6.72 );
... または、C の v_args 呼び出し規則を探している可能性があります。
カラーコードをサポートする C++ 11
#pragma once
#include <iostream>
#include <string>
const std::string RED = "\e[0;91m";
const std::string BLUE = "\e[0;96m";
const std::string YELLOW = "\e[0;93m";
class Logger {
private:
enum class Severity { INFO, WARN, ERROR };
static void print_colored(const char *log, Severity severity) {
const char *color_code = nullptr;
switch (severity) {
case Severity::INFO:
color_code = BLUE.c_str();
break;
case Severity::WARN:
color_code = YELLOW.c_str();
break;
case Severity::ERROR:
color_code = RED.c_str();
break;
}
std::cout << "\033" << color_code << log << "\033[0m -- ";
}
template <class Args> static void print_args(Args args) {
std::cout << args << " ";
}
public:
template <class... Args> static void info(Args &&...args) {
print_colored("[INFO] ", Severity::INFO);
int dummy[] = {0, ((void)print_args(std::forward<Args>(args)), 0)...};
std::cout << std::endl;
}
template <class... Args> static void warn(Args &&...args) {
print_colored("[WARN] ", Severity::WARN);
int dummy[] = {0, ((void)print_args(std::forward<Args>(args)), 0)...};
std::cout << std::endl;
}
template <class... Args> static void error(Args &&...args) {
print_colored("[ERROR]", Severity::ERROR);
int dummy[] = {0, ((void)print_args(std::forward<Args>(args)), 0)...};
std::cout << std::endl;
}
};
提供される引数の数の範囲がわかっている場合は、次のような関数のオーバーロードをいつでも使用できます。
f(int a)
{int res=a; return res;}
f(int a, int b)
{int res=a+b; return res;}
等々...
他の人が言っているように、Cスタイルのvarargs。ただし、デフォルトの引数を使用して同様のことを行うこともできます。
今は可能です...boost any とテンプレートを使用します。この場合、引数の型を混在させることができます
#include <boost/any.hpp>
#include <iostream>
#include <vector>
using boost::any_cast;
template <typename T, typename... Types>
void Alert(T var1,Types... var2)
{
std::vector<boost::any> a( {var1,var2...});
for (int i = 0; i < a.size();i++)
{
if (a[i].type() == typeid(int))
{
std::cout << "int " << boost::any_cast<int> (a[i]) << std::endl;
}
if (a[i].type() == typeid(double))
{
std::cout << "double " << boost::any_cast<double> (a[i]) << std::endl;
}
if (a[i].type() == typeid(const char*))
{
std::cout << "char* " << boost::any_cast<const char*> (a[i]) <<std::endl;
}
// etc
}
}
void main()
{
Alert("something",0,0,0.3);
}
すべての引数が const で同じ型の場合、initializer_list を使用することもできます
int fun(int n_args, ...) {
int *p = &n_args;
int s = sizeof(int);
p += s + s - 1;
for(int i = 0; i < n_args; i++) {
printf("A1 %d!\n", *p);
p += 2;
}
}
プレーンバージョン