2016-11-03 50 views
4

我發現有時函數有很多參數。這些參數中的很多參數都是可選的,有時候一組這些選項通常來自單個其他對象(所以最終你會做到foo(Object.GetN(), Object.GetM(), Object.GetK()))。對付它的常用方法是創建不同的情況不同的重載,它可能被稱爲:這個方法已經有了名字嗎?

foo(int n, int m, int k /*, and on and on*/); 
foo(bool b, int m/*, ...*/); 
foo(int m, int k/*, ...*/); 
foo(Object_t object/*, ...*/); 
//... 

這裏的問題是,它的參數是不是特別直觀,你可以得到相當的驚喜,當你稱之爲你想要的超負荷。

最近我有一個想法,讓它更容易讓函數調用正確,讓我自己在處理這些具有多種被調用方法的函數時更容易。這個解決方案並沒有涵蓋所有可能的需求,但它對我來說效果很好。

而不是創建一切的重載,我會創建一個函數,只需要可變數量的參數,然後提取可能的參數在函數內使用。至於參數,我會將它們包裝在爲這些函數創建的輔助類中。這將允許用戶聲明每個整數,布爾值或字符串或者什麼意思,而不是依賴函數簽名中的位置信息。

代替foo(n, m)(其中由變量的名稱會以上建議可能的錯誤),你會打電話foo(OptN(n), OptM(m))使得它完全清楚每個參數將被用於和更難有一個參數被誤解。

如果任何人對這種可能的實現感興趣,我將在最後包含一個MCVE。

我從來沒有見過或聽說過這種技術,但我也很難相信我是第一個想到它的人。所以,最後,我的問題僅僅是這個技術有沒有名字?

如果它沒有名稱,我一直在調用這些函數的聲明函數,因爲你聲明每個參數明確代表的是什麼,而不是依賴於參數賦予其含義的位置函數。

MCVE:

#include <iostream> 
#include <utility> 

struct Option1 
{ 
    Option1(bool b):b(b){} 
    bool b; 
    bool operator()() const {return b;} 
}; 

struct Option2 
{ 
    Option2(int n):n(n){} 
    int n; 
    int operator()() const {return n;} 
}; 

struct Group : Option1, Option2 
{ 
    Group(bool b, int n):Option1(b), Option2(n){} 
}; 

/* 
* Get the option from what the user gave us. 
*/ 
template <class OptionType, class OptionsGetter, class RType> 
auto GetOptionImpl(const OptionsGetter & options_getter, 
        const RType&, std::true_type) -> 
    decltype(((const OptionType&)options_getter)()) 
{ 
    return ((const OptionType&)options_getter)(); 
} 

/* 
* Get the default value specified since the user didn't pass 
* in that option 
*/ 
template <class OptionType, class OptionsGetter, class RType> 
RType GetOptionImpl(const OptionsGetter&, const RType & d, std::false_type) 
{ 
    return d; 
} 

/** 
* Returns the value of the option OptionType if the user 
* passed that in (inside OptionsGetter) and returns the 
* default value if they didn't pass it in. 
*/ 
template <class OptionType, class OptionsGetter, class RType> 
auto GetOption(const OptionsGetter & oOptionsGetter, 
       const RType & oDefault) -> 
    decltype(std::declval<OptionType>()()) 
{ 
    return GetOptionImpl<OptionType>(oOptionsGetter, oDefault, 
            std::is_base_of<OptionType, OptionsGetter>()); 
} 

template <class ... Params> 
void foo(Params ... params) 
{ 
    struct ParamsGetter : Params... 
    { 
     ParamsGetter(Params ... p): Params(p)...{} 
    } params_getter(params...); 

    if(GetOption<Option1>(params_getter, false)) 
     std::cout << "Option 1 was true "; 
    else 
     std::cout << "Option 1 was false "; 
    std::cout << "Option 2: " << GetOption<Option2>(params_getter, 3) << '\n'; 
} 

int main() 
{ 
    foo(Option1{true}, Option2{22}); 
    foo(); 
    foo(Option2{1}); 
    foo(Group(true, 2)); 
} 

輸出:

Option 1 was true Option 2: 22 
Option 1 was false Option 2: 3 
Option 1 was false Option 2: 1 
Option 1 was true Option 2: 2 
+0

不明白爲什麼你需要所有的東西.... – jpo38

+0

這是你在尋找什麼? https://en.wikipedia.org/wiki/Named_pa​​rameter –

+0

你可以使用我在這裏描述的技術獲得相同的結果:http://stackoverflow.com/questions/40381918/preferred-mechanism-to-attach-a-類型到標量 –

回答

1

正如在評論中提到,這個概念叫做命名參數。請參閱關於wikipedia的說明,以及例如proposal以在C++中介紹它。

+0

閱讀文章,它看起來在C++中被稱爲_named參數idiom_,文章描述瞭如何通過將方法鏈接在一起來實現。我想現在用C++ 11的variadic模板已經有點過時了 – SirGuy

0

我認爲這通常被稱爲不透明typedef強typedef。這個想法是要解決你描述的確切問題 - 你有類型有整數值,但你想明確地設置它們。

欲瞭解更多關於這個概念的動機,你可以看到this proposal包括在語言和Boost's implementation它。

+0

'opaque typedef'似乎是解決方案的一部分,但是命名參數習慣用法似乎對可選參數有幫助。 – SirGuy

+0

@GuyGreer命名參數實際上是命名參數。那就是'foo(.a = 7,.b = 4)'(假設提案獲得批准)或其他一些機制,如'foo()。a(7).b(4)'。你沒有命名參數,你正在鍵入(如在類型系統中)它們。 – Barry

+0

這被稱爲_named參數idiom_,包含模擬其他語言直接支持的行爲。 – SirGuy

相關問題