2016-07-19 8 views
6

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

int foo(int x=42); 

Если это называется другими, как это:

int bar(int x=42) { return foo(x); } 
int moo(int x=42) { return bar(x); } 

Это, конечно, просто надуманный пример. Тем не менее, иногда у меня очень похожая ситуация. Параметр только что передается с самого высокого уровня (moo) до самого низкого уровня, и только там он фактически используется. Плохая вещь об этом заключается в том, что когда я изменяю foo, чтобы иметь значение по умолчанию, отличное от 42, мне пришлось бы искать всех абонентов и соответственно менять значение по умолчанию.

Есть ли какой-нибудь шаблон/идиома, чтобы избежать этой ситуации?

Единственное простое решение, которое приходит на ум, это

int bar()  { return foo(); } 
int bar(int x) { return foo(x); } 

Однако, как я немного ленив и в реальном коде это приведет к довольно кода дублирования, я хотел бы избежать этого.

+0

Я бы сказал, что вы не должны использовать значение по умолчанию для параметров по умолчанию. Для тех, кто использовал бы переменные const с переменными имен. Значения параметров по умолчанию хороши для таких вещей, как * sane * значения инициализации (zero/nullptr/false). – Galik

ответ

2

Практические общие решения включают в себя:

  • Используйте Optional_ класс для аргумента, такие как boost::optional или сделай сам эквивалент.

  • Назовите значение по умолчанию (и используйте имя в определениях функций оболочки).

  • Перегрузите каждую функцию обертки, как вы показали в вопросе.

  • Просто повторите значение по умолчанию в определениях функций оболочки, но это нарушает принцип DRY, не повторяйте себя.


В comment else-thread Tobi вызывает случай обертки asdf определяются как

int asdf(int x=42,int y=42){ return foo(x)+foo(y);} 

, используя Optional_ класс:

auto foo(Optional_<int> x) 
    -> int 
{ return (x.is_empty()? 42 : x.value()); } 

auto asdf(Optional_<int> x = {}, Optional_<int> y = {}) 
    -> int 
{ return foo(x) + foo(y); } 

Использование имени значения по умолчанию:

Использование перегруженных:

auto foo(int x = 42) 
    -> int 
{ return x; } 

auto asdf() 
    -> int 
{ return foo() + foo(); } 

auto asdf(int x) 
    -> int 
{ return foo(x) + foo(); } 

auto asdf(int x, int y) 
    -> int 
{ return foo(x) + foo(y); } 

Стоит отметить, что asdf не может быть легко определен как шаблон функции переадресации аргументов. Кроме того, такой шаблон не может быть легко определен в отдельной единицы перевода, и его адрес невозможен. По этим причинам я не включил это возможное решение в список пулей: это очень ограниченное, а не общее решение.

+0

'boost :: optional' * или *' std :: optional' сейчас. – lorro

+0

Я не уверен, почему я сам не реализовал вариант именованной константы ... Иногда я просто думаю слишком сложно (даже для C++) – user463035818

6

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

  1. перегружать функции
  2. Определить константу

Таким образом, вариант 1 будет выглядит, как показано ниже:

int foo(int x=42); 
int bar(int x) { return foo(x); } 
int moo(int x) { return bar(x); } 
int bar() { return foo(); } 
int moo() { return bar(); } 

А, вариант 2 будет немного короче:

constexpr int FOO_DEFAULT = 42; 
int foo(int x=FOO_DEFAULT); 
int bar(int x=FOO_DEFAULT) { return foo(x); } 
int moo(int x=FOO_DEFAULT) { return bar(x); } 

Я бы использовал o ption-1 для случаев с небольшим числом значений по умолчанию (как один значение по умолчанию), вариант-2 для случаев, когда у вас есть довольно много значений по умолчанию - как foo(int a, int b = 3, std::string c = "wow", float pi = 3.14)

+0

Приобретено, несмотря на то, что у вас есть два варианта выбора, что неверно. :) –

+0

При рассмотрении, удалили upvote из-за идентификаторов ALL UPPERCASE (я не заметил, извините). Все прописные буквы, вероятно, конфликтуют с именами макросов. Таким образом, это учит C++ нехорошей идиоме: это нормально в Java и требуется в Python, но очень плохое в C++. –

+0

@ Cheersandhth.-Alf Я верю, что OP, если использовать мое предложенное решение, выберет имя в соответствии со своим стандартом/стилем кодирования (если есть). Кстати, мое мнение по этой очень конкретной теме заключается в том, что тот же самый стиль должен использоваться для переменных-макросов и константных переменных - потому что логически - есть одно и то же, они используются для достижения той же цели. Итак, это совершенно противоположное мнение для вашего ... – PiotrNycz

3

Вы можете избежать дублирования:

template<typename... T> 
    auto bar(T&&... t) { return foo(std::forward<T>(t)...); } 

Но это не улучшение ИМХО. Просто перестаньте лениться и определите перегрузки, которые вызывают foo(), когда аргументы не предусмотрены.

+1

** - 1 ** Это изменяет характер 'bar'. Теперь его адрес невозможен. Нельзя (практически) реализовать его в отдельно скомпилированном модуле. Вызвав его с нечетным количеством аргументов или типов аргументов, вместо простых проверок можно получить ошибки создания шаблона. –

+1

Как я уже сказал, это не улучшение. Но это ** делает ** избегать дублирования, чего OP хотел избежать. –

+1

Я думаю, что это не сработает, если у меня есть: 'int asdf (int x = 42, int y = 42) {return foo (x) + foo (y);}' – user463035818