Указание параметра по умолчанию при вызове функции C++
Предположим, у меня есть такой код:
void f(int a = 0, int b = 0, int c = 0)
{
//...Some Code...
}
как вы, очевидно, можете видеть выше с моим кодом, параметры a
,b
и 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)
, потому что c
будет оцениваться как b
, что я не хочу, особенно если c
неправильный тип. Есть ли способ для вызывающей функции указать в C++, чтобы использовать любое значение параметра по умолчанию для функции в любой заданной позиции, не ограничиваясь возвращением от последнего параметра к none? Есть ли зарезервированное ключевое слово для достижения этого или, по крайней мере, обходной путь? Пример могу привести бы:
f(a, def, c) //Where def would mean default.
5 ответов
в качестве обходного пути вы можете (ab)использовать boost::optional
(до std::optional
из C++17):
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);
для этого нет зарезервированного слова, и 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
обратите внимание, что невозможно указать пользовательское значение для z без предоставления значения для x и y. Это потому, что C++ делает не поддерживает синтаксис вызова функции, такой как printValues (,, 3). Это два главных следствия:--6-->
1) все параметры по умолчанию должны быть самыми правыми параметрами. Этот следование не разрешено:
void printValue(int x=10, int y); // not allowed
2) если существует несколько параметров по умолчанию, самый левый по умолчанию параметр должен быть тем, который, скорее всего, будет явно задан пользователь.
не совсем то, что вы просите, но вы можете использовать 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.С.: извините за мой плохой английский.
Если бы все параметры функции были distinct типы, вы можете узнать, какие параметры были переданы, а какие нет, и выбрать значение по умолчанию для последнего.
для того чтобы достигнуть определенного требования к типа, вы можете обернуть ваши параметры и передать его к шаблону функции variadic. Тогда даже порядок аргумента больше не имеет значения:
#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
редактировать: этот вопрос был старше, и я нашел его, когда еще вопрос был закрыт как дубликат (за плагиат).
у вас уже есть принятый ответ, но вот еще один обходной путь (который - я считаю - имеет преимущества над другими предлагаемого решения):
вы можете ввести сильные аргументы:
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);
}
плюсы:
- это просто и легко поддерживать (одна строка на аргумент).
- предоставляет естественная точка для дальнейшего сужения API (например, "бросок, если значение B отрицательно").
- это не мешает (работает с конструкцией по умолчанию, работает с intellisense / auto-complete / что угодно, как и любой другой класс)
- это самодокументируемыми.
- это так же быстро, как и родная версия.
недостатки:
- увеличивает загрязнение имени (лучше поместить все это в пространство имен.)
- хотя это просто, это все еще больше кода для поддержания (чем просто определение функции напрямую).
- это может поднять несколько бровей (подумайте о добавлении комментария о том, почему нужен сильный ввод)