2016-08-06 8 views
9

Пусть у меня есть такой код:Задание параметров по умолчанию при вызове 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. 
+0

Вы можете посмотреть на именованных параметрах. Есть несколько трюков, чтобы иметь эту функцию в C++ как BOOST_PARAMETER_FUNCTION, а затем указать, какой параметр вы даете. – Jarod42

+0

Если вам кажется, что вам нужно это сделать, у вас может быть недостаток дизайна. Я бы предложил вам переоценить его. –

+0

@RobK Это был просто вопрос любопытства. –

ответ

6

Как решить эту проблему, вы можете (аb) использовать 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); 
+1

Это хорошее оптимальное решение. Мне это действительно нравится и я могу его использовать. +1 –

12

Существует не зарезервированное слово для этого, и 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 

Обратите внимание, что невозможно предоставить определенное пользователем значение для г без подачи также значение для х и у. Это связано с тем, что C++ не поддерживает синтаксис вызова функции, такой как printValues ​​(,, 3). Это имеет два основных последствия:

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

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

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

+4

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

+0

ОК. если вы верите в то, что он говорит, тогда на ваш вопрос ответят. вам нужно структурировать свою функцию, чтобы обеспечить нужное поведение. Например, если пользователь вводит какое-то недопустимое значение, возможно, -1, то используйте его по умолчанию. – Alejandro

+1

случайно нажат ввод. комментарий отредактировал – Alejandro

5

Не совсем то, что вы просите, но вы можете использовать std::bind(), чтобы исправить значение параметра.

Somethink как

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

+1

Спасибо за обходной путь. Я действительно могу использовать это. +1 –

+0

@ АрнавБорбора - добро пожаловать; изменил свой ответ, следующий за вашим комментарием; надеясь, что это поможет. – max66

1

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

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

#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

Спасибо за ваш ответ, но вариативные шаблоны .... +1, в любом случае и спасибо за ссылку –

+0

@ArnavBorborah, что не так с вариативными шаблонами? –

+1

Не слишком ли многоточие? –

0

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

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

Вы можете сильного типа аргументы:

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/независимо от любого другого класса)
  • это самодокументирующее.
  • это так же быстро, как и родная версия.

Недостатки:

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