Указание параметра по умолчанию при вызове функции 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 ++, чтобы использовать любое значение параметра по умолчанию для функции в любой заданной позиции, не ограничиваясь переходом от последнего параметра к нулю? Есть ли зарезервированное ключевое слово для достижения этого или хотя бы обходной путь? Пример, который я могу привести:

f(a, def, c) //Where def would mean default.
 Rob K28 сент. 2016 г., 19:10
Если вам кажется, что вам нужно это сделать, возможно, у вас есть недостаток дизайна. Я бы посоветовал вам переоценить это.
 Jarod4207 авг. 2016 г., 00:52
Вы можете посмотреть на именованные параметры. Есть несколько приемов, позволяющих использовать эту функцию в C ++ как BOOST_PARAMETER_FUNCTION, а затем указать, какой параметр вы передаете.
 Arnav Borborah16 окт. 2016 г., 23:40
@RobK Это был просто вопрос любопытства.

Ответы на вопрос(5)

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 .: извините за мой плохой английский.

 Arnav Borborah06 авг. 2016 г., 20:27
Спасибо за обходной путь. Я действительно мог бы использовать это. +1
 max6606 авг. 2016 г., 20:32
@ArnavBorborah - пожалуйста; изменил мой ответ рядом с вашим комментарием; надеясь, что это поможет.

отчетливый типы, вы можете узнать, какие параметры были переданы, а какие нет, и выбрать значение по умолчанию для последних.

Для достижения четкого требования к типу вы можете обернуть ваши параметры и передать их в шаблон функции с переменным числом аргументов. Тогда даже порядок аргументов больше не имеет значения:

#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

живой пример

 m.s.06 авг. 2016 г., 21:56
@ArnavBorborah что не так с вариадическими шаблонами?
 Arnav Borborah06 авг. 2016 г., 21:59
Не безопасны ли многоточия?
 m.s.06 авг. 2016 г., 22:00
@ArnavBorborah небезопасно каким образом? кстати: шаблон переменнойне такой же как эллипсис
 Arnav Borborah06 авг. 2016 г., 21:49
Спасибо за ответ, но вариадические шаблоны .... +1, во всяком случае, и спасибо за ссылку
 Arnav Borborah06 авг. 2016 г., 22:01
О, хорошо, я их запутал, Другой ответ переполнения стека, упомянул 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). Это имеет два основных последствия:

1) Все параметры по умолчанию должны быть самыми правыми параметрами. Следующее не допускается:

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

2) Если существует более одного параметра по умолчанию, крайний левый параметр по умолчанию должен быть тем, который, скорее всего, будет явно установлен пользователем.

 Arnav Borborah06 авг. 2016 г., 20:17
Я уже прочитал вашу ссылку, но я уже знаю, что вы говорите прямо сейчас. Спасибо за ваш вклад, хотя, и если лучший ответ не появляется, я отмечу это как правильный.
 Alejandro06 авг. 2016 г., 20:19
Хорошо. если вы верите тому, что он говорит, то на ваш вопрос ответят. вам нужно структурировать свою функцию, чтобы включить желаемое поведение. Например, если пользователь вводит какое-то недопустимое значение, возможно, -1, тогда используйте его по умолчанию.

редактировать: этот вопрос был старше, и я нашел его, когда другой вопрос был закрыт как дубликат (для плагиата).

У вас уже есть принятый ответ, но вот еще один обходной путь (который, я считаю, имеет преимущества перед другими предлагаемыми обходными путями):

Вы можете строго набрать аргументы:

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 (например, «throw, если значение B отрицательно»).это не мешает (работает с конструкцией по умолчанию, работает с intellisense / auto-complete / любым другим классом)это самодокументирование.это так же быстро, как родная версия.

Недостатки:

увеличивает загрязнение имени (лучше поместить все это в пространство имен).хотя и прост, он все же требует больше кода для поддержки (чем просто определение функции напрямую).это может поднять несколько бровей (рассмотрите добавление комментария о том, почему необходима строгая типизация)
Решение Вопроса

В качестве обходного пути вы можете (ab) использоватьboost::optional (до тех порstd::optional из с ++ 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);
 Arnav Borborah07 авг. 2016 г., 15:17
Это хорошее оптимальное решение. Я действительно люблю это и могу использовать это. +1

Ваш ответ на вопрос