web-dev-qa-db-ja.com

C ++関数を呼び出すときにデフォルトパラメータを指定する

次のようなコードがあるとします。

_void f(int a = 0, int b = 0, int c = 0)
{
    //...Some Code...
}
_

上記のコードで明らかなように、パラメーターab、およびcのデフォルトのパラメーター値は0です。次に以下のメイン関数を見てください。

_int main()
{
   //Here are 4 ways of calling the above function:
   int a = 2;
   int b = 3;
   int c = -1;

   f(a, b, c);
   f(a, b);
   f(a); 
   f();
   //note the above parameters could be changed for the other variables
   //as well.
}
_

これで、パラメータをスキップしてデフォルト値のままにすることはできないことがわかりました。その値はその位置のパラメータとして評価されるためです。つまり、f(a,c)を呼び出すことはできません。これは、cbとして評価されるためです。これは、特に次の場合に必要です。 cは間違ったタイプです。呼び出し元の関数がC++で指定し、最後のパラメーターからなしに戻ることに制限されることなく、任意の位置で関数にあるデフォルトのパラメーター値を使用する方法はありますか?これを達成するための予約済みのキーワード、または少なくとも回避策はありますか?私が与えることができる例は次のようになります:

_f(a, def, c) //Where def would mean default.
_
14
Arnav Borborah

回避策として、boost::optionalを(ab)使用することができます(c ++ 17からstd::optionalまで):

void f(boost::optional<int> oa = boost::none,
       boost::optional<int> ob = boost::none,
       boost::optional<int> oc = boost::none)
{
    int a = oa.value_or(0); // Real default value go here
    int b = ob.value_or(0); // Real default value go here
    int c = oc.value_or(0); // Real default value go here

    //...Some Code...
}

そしてそれを呼びます

f(a, boost::none, c);
11
Jarod42

このための予約語はなく、f(a,,c)も無効です。示されているように、右端のオプションパラメータの数は省略できますが、そのような中央のパラメータは省略できません。

http://www.learncpp.com/cpp-tutorial/77-default-parameters/

上記のリンクから直接引用:

複数のデフォルトパラメータ

関数は複数のデフォルトパラメータを持つことができます:

void printValues(int x=10, int y=20, int z=30)
{
    std::cout << "Values: " << x << " " << y << " " << z << '\n';
}

次の関数呼び出しがあるとします。

printValues(1, 2, 3);
printValues(1, 2);
printValues(1);
printValues();

次の出力が生成されます。

Values: 1 2 3
Values: 1 2 30
Values: 1 20 30
Values: 10 20 30

Xとyの値も指定せずに、zのユーザー定義値を指定することは不可能であることに注意してください。これは、C++がprintValues(, 3)などの関数呼び出し構文をサポートしていないためです。これには2つの大きな影響があります。

1)すべてのデフォルトパラメータは右端のパラメータである必要があります。以下は許可されていません。

void printValue(int x=10, int y); // not allowed

2)複数のデフォルトパラメータが存在する場合、左端のデフォルトパラメータは、ユーザーが明示的に設定する可能性が最も高いパラメータである必要があります。

15
Alejandro

正確には要求どおりではありませんが、std::bind()を使用してパラメーターの値を修正できます。

のように考える

_#include <functional>

void f(int a = 0, int b = 0, int c = 0)
{
    //...Some Code...
}

int main()
{
   //Here are 4 ways of calling the above function:
   int a = 2;
   int b = 3;
   int c = -1;

   f(a, b, c);
   f(a, b);
   f(a); 
   f();
   //note the above parameters could be changed for the other variables
   //as well.

   using namespace std::placeholders;  // for _1, _2

   auto f1 = std::bind(f, _1, 0, _2);

   f1(a, c); // call f(a, 0, c);

   return 0;
}
_

std::bind()を使用すると、デフォルトのパラメーター値とは異なる値、またはデフォルト値のないパラメーターの値を修正できます。

std::bind()はC++ 11からのみ利用可能であることを考慮に入れてください。

p.s .:英語が下手でごめんなさい。

7
max66

関数のすべてのパラメーターがdistinctタイプである場合、渡されたパラメーターと渡されなかったパラメーターを見つけて、後者のデフォルト値を選択できます。

個別の型の要件を達成するために、パラメーターをラップして可変個引数関数テンプレートに渡すことができます。そうすれば、引数の順序でさえもう重要ではありません。

#include <Tuple>
#include <iostream>
#include <type_traits>

// -----
// from http://stackoverflow.com/a/25958302/678093
template <typename T, typename Tuple>
struct has_type;

template <typename T>
struct has_type<T, std::Tuple<>> : std::false_type {};

template <typename T, typename U, typename... Ts>
struct has_type<T, std::Tuple<U, Ts...>> : has_type<T, std::Tuple<Ts...>> {};

template <typename T, typename... Ts>
struct has_type<T, std::Tuple<T, Ts...>> : std::true_type {};

template <typename T, typename Tuple>
using Tuple_contains_type = typename has_type<T, Tuple>::type;
//------


template <typename Tag, typename T, T def>
struct Value{
    Value() : v(def){}
    Value(T v) : v(v){}
    T v; 
};

using A = Value<struct A_, int, 1>;
using B = Value<struct B_, int, 2>;
using C = Value<struct C_, int, 3>;


template <typename T, typename Tuple>
std::enable_if_t<Tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple t)
{
    return std::get<T>(t);
}

template <typename T, typename Tuple>
std::enable_if_t<!Tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple)
{
    return T{};
}

template <typename InputTuple, typename... Params>
auto getValueOrDefault(std::Tuple<Params...>, InputTuple t)
{
    return std::make_Tuple(getValueOrDefaultImpl<Params>(t)...);
}

template <typename... Params, typename ArgTuple>
auto getParams(ArgTuple argTuple) 
{
    using ParamTuple = std::Tuple<Params...>;
    ParamTuple allValues = getValueOrDefault(ParamTuple{}, argTuple);
    return allValues;
}

template <typename... Args>
void f(Args ... args)
{
    auto allParams = getParams<A,B,C>(std::make_Tuple(args...));
    std::cout << "a = " << std::get<A>(allParams).v << " b = " << std::get<B>(allParams).v << " c = " << std::get<C>(allParams).v << std::endl;
}

int main()
{
   A a{10};
   B b{100};
   C c{1000};

   f(a, b, c);
   f(b, c, a);
   f(a, b);
   f(a); 
   f();
}

出力

a = 10 b = 100 c = 1000
a = 10 b = 100 c = 1000
a = 10 b = 100 c = 3
a = 10 b = 2 c = 3
a = 1 b = 2 c = 3

live example

1
m.s.

編集:この質問は古く、別の質問が重複として閉じられたときに見つかりました(盗用のため)。

あなたはすでに受け入れられた答えを持っていますが、ここに別の回避策があります(それは-私は信じています-他の提案された回避策よりも利点があります):

引数を厳密に入力できます。

struct A { int value = 0; };
struct B { int value = 2; };
struct C { int value = 4; };

void f(A a = {}, B b = {}, C c = {}) {}
void f(A a, C c) {}

int main()
{
    auto a = 0;
    auto b = -5;
    auto c = 1;

    f(a, b, c);
    f(a, C{2});
    f({}, {}, 3);
}

利点:

  • シンプルでメンテナンスが簡単です(引数ごとに1行)。
  • aPIをさらに制限するための自然なポイントを提供します(たとえば、「Bの値が負の場合にスローする」)。
  • 邪魔になりません(デフォルトの構造で動作し、インテリセンス/オートコンプリート/他のクラスと同じくらい良いもので動作します)
  • それは自己文書化です。
  • ネイティブバージョンと同じくらい高速です。

短所:

  • 名前の汚染を増やします(これをすべて名前空間に入れる方がよい)。
  • 単純ですが、(関数を直接定義するだけでなく)維持するコードがさらに多くなります。
  • それはいくつかの眉を上げるかもしれません(強い型付けが必要な理由についてコメントを追加することを検討してください)
0
utnapistim