2011-11-22 43 views
2

我們試圖重構我們的代碼,我們想要的改進之一如下:許多函數有許多參數,但其中很多參數共享一個公共子集。所以,我們想創建一個可以將它們分組的結構。問題是一些函數需要一些參數爲const,有些則不是。其中一些函數必須能夠調用提供此參數分組結構的這些函數的子集,但是在以下限制下:被調用函數不能「降級」此結構的常量(請參見下面的示例)。實現此結構的所有必需變體解決了這個問題,但並不優雅。我們正在努力解決辦法之一是使用模板,e.g:函數參數分組和常量

template<class A, class B, class C> 
struct my_container 
{ 
    A a; 
    B b; 
    C c; 
}; 

void foo1(my_container<int, char, float const> & my_cont) 
{ 
} 

void foo2(my_container<int const, char, float const> & my_cont) 
{ 
    // This should NOT be allowed: we do mind something being const to be treated by the 
    // called function as non-const. 
    foo1(my_cont); 
} 

void foo3(my_container<int, char, float> & my_cont) 
{ 
    // This should be allowed: we don't mind something being non-const to be treated by the 
    // called function as const. 
    foo2(my_cont); 
} 

我們的問題是,foo2的要求foo1沒有編譯器抱怨,和我們想的完全相反。這甚至可以用模板來實現嗎?還有其他技術嗎?

+0

在我看來,如果常量單獨變化,也許只是不要試圖對這些論點進行分組。 – visitor

+0

該模板似乎表明您正試圖將三個無關的值混合在一起? - 如果你有一個真正的相關功能類,我不認爲人們通常會試圖使它部分可修改。例如,你不想讓功能只修改Point實例的x座標,而不是y。如果它很重要,我想你會把它們作爲單獨的參數傳遞。 – UncleBens

回答

0

我的解決方案有點元編程。看起來很醜陋,而不是throughoutly測試,但不管怎麼說: 這樣,一切都應該在一般的工作「開箱即用」

#include <iostream> 
#include <boost/type_traits.hpp> 

template<class A, class B, class C> 
struct my_container 
{ 
    A a; 
    B b; 
    C c; 

    template<typename An, typename Bn, typename Cn> 
    operator my_container<An,Bn,Cn>&() 
    { 
     /* First, check whether compatible at all */ 
     BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<A>::type, typename boost::remove_cv<An>::type>::value)); 
     BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<B>::type, typename boost::remove_cv<Bn>::type>::value)); 
     BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<C>::type, typename boost::remove_cv<Cn>::type>::value)); 
     /* Enforce const'ness */ 
     BOOST_STATIC_ASSERT(!boost::is_const<A>::value || boost::is_const<An>::value); 
     BOOST_STATIC_ASSERT(!boost::is_const<B>::value || boost::is_const<Bn>::value); 
     BOOST_STATIC_ASSERT(!boost::is_const<C>::value || boost::is_const<Cn>::value); 

     return *reinterpret_cast< my_container<An,Bn,Cn>* >(this); 
    } 
}; 

void foo1(my_container<int, char, float const> & my_cont) 
{ 
} 

void foo2(my_container<int const, char, float const> & my_cont) 
{ 
    // This should NOT be allowed: we do mind something being const to be treated by the 
    // called function as non-const. 
    //foo1(my_cont); /// BOOST_STATIC_ASSERT fails! Hurray! 
} 

void foo3(my_container<int, char, float> & my_cont) 
{ 
    // This should be allowed: we don't mind something being non-const to be treated by the 
    // called function as const. 
    foo2(my_cont); /// No complaints! Hurray! 
} 

int main(int argc, char* argv[]) 
{ 
    my_container<int,char,float> foobar; 

    foo3(foobar); 

    return 0; 
} 
1

都不應該工作。模板的不同實例是未經過處理的類型,它們之間沒有隱式轉換。所以 my_container<int, char, float const>,my_container<int const, char, float const>my_container<int, char, float>都是不相關的 類型,它們之間沒有隱式轉換。

應該可以制定出使用繼承的東西,使用 元編程技巧,以確定你繼承了什麼,但我不 知道如何,我懷疑,這將是更多的努力比它的價值。

+0

你是對的,但我很快就對它進行了測試。正如所料,兩者都不能與g ++ 4.4一起工作。或MSVC 2010. – zerm

1

不接壤未定義行爲,這可以通過間接的另一個層面上實現。添加引用原始成員的視圖類。可以隱式添加Constness,但不能刪除。

template<class A, class B, class C> 
struct my_container 
{ 
    A a; 
    B b; 
    C c; 
}; 

template <class A, class B, class C> 
class my_container_view 
{ 
    A* a_; 
    B* b_; 
    C* c_; 

public: 
    template <class A_, class B_, class C_> 
    my_container_view(my_container<A_, B_, C_>& source): 
     a_(&source.a), b_(&source.b), c_(&source.c) 
    {} 
    template <class A_, class B_, class C_> 
    my_container_view(my_container_view<A_, B_, C_>& source): 
     a_(&source.a()), b_(&source.b()), c_(&source.c()) 
    {} 
    A& a() const { return *a_; } 
    B& b() const { return *b_; } 
    C& c() const { return *c_; } 
}; 

void foo1(my_container_view<int, char, float const> my_cont) 
{ 
    my_cont.a() = 10; 
    my_cont.b() = 'a'; 
    my_cont.c() /*= 3.14*/; 
} 

void foo2(my_container_view<int const, char, float const> my_cont) 
{ 
    my_cont.a() /*= 10*/; 
    my_cont.b() = 'a'; 
    my_cont.c() /*= 3.14*/; 

    //foo1(my_cont); //not allowed 
} 

void foo3(my_container_view<int, char, float> my_cont) 
{ 
    my_cont.a() = 10; 
    my_cont.b() = 'a'; 
    my_cont.c() = 3.14; 
t 
    foo2(my_cont); 
} 

int main() 
{ 
    my_container<int, char, float> mc; 
    foo1(mc); 
    foo2(mc); 
    foo3(mc); 
} 

(我有我的懷疑,不過,這多少是值得通常帶班,要麼你可以修改它的所有成員 - 你只是不修改你不想要的那些 - ,或者你不能修改任何,如果你想要這個級別的控制,你寧願單獨傳遞每個參數 - 與你正在做的事情相反)。

+0

我想知道與您的解決方案相比,您的解決方案有哪些優點,除了不需要增強功能之外?關於你的評論,我認爲OP在他的問題的前兩句中就明確了他的用例。 – zerm

+0

@zerm:我懷疑技術上不相關的類型之間的reinterpret_casting在技術上是未定義的行爲,儘管我不明白爲什麼它在實踐中不起作用。 - 關於疑問:「問題是一些函數需要一些參數是const,有些則不是。」 - 如果結果物體真的形成一個整體,我還沒有遇到過這樣的困境。或者,如果您想要更細粒度的常量控制 - 分別傳遞每個成員,那麼每個成員都可以有自己的常量。 – UncleBens

+0

與reinterpret_cast好點,我解決了我的解決方案,因爲這兩種類型需要相同(除const'ness之外),而不僅僅是可轉換 - 這種方式,我沒有看到爲什麼reinterpret_cast會導致任何問題的原因?我會檢查std說什麼。 – zerm