2012-06-28 61 views
1

警告:需要長期介紹以解釋問題。在Vandevoorde和約祖蒂斯的CH 16.1首次描述的命名模板參數成語可以方便地寫入與Boost.ParameterBoost.Parameter:命名模板參數與CRTP結合使用

#include <iostream> 
    #include <typeinfo> 
    #include <boost/parameter.hpp> 
    #include <boost/static_assert.hpp> 

    struct DefaultPolicy1 {}; 
    struct DefaultPolicy2 {}; 

    typedef boost::parameter::void_ DefaultSetter; 

    BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy1_is) 
    BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy2_is) 

    typedef boost::parameter::parameters< 
      boost::parameter::optional<tag::Policy1_is>, 
      boost::parameter::optional<tag::Policy2_is> 
    > PolicySelector; 

    template 
    < 
      class PolicySetter1 = DefaultSetter, 
      class PolicySetter2 = DefaultSetter 
    > 
    class BreadSlicer 
    { 
      typedef typename PolicySelector::bind< 
        PolicySetter1, 
        PolicySetter2 
      >::type Policies; 

    public: 
      // extract policies: 
      typedef typename boost::parameter::value_type< 
        Policies, tag::Policy1_is, DefaultPolicy1 
      >::type P1; 

      typedef typename boost::parameter::value_type< 
        Policies, tag::Policy2_is, DefaultPolicy2 
      >::type P2; 
    }; 

上面的代碼允許通過命名來覆蓋arbritrary順序的BreadSlicer可選模板參數他們Policy1_isPolicy2_is。這使得使用許多默認參數進行基於策略的設計非常方便。

int main() 
{ 
     typedef BreadSlicer<> B1; 

     // can override any default policy 
     typedef BreadSlicer< Policy1_is<int> > B2; 
     typedef BreadSlicer< Policy2_is<char> > B3; 

     // order of policy-setting is irrelevant 
     typedef BreadSlicer< Policy1_is<int>, Policy2_is<char> > B4; 
     typedef BreadSlicer< Policy2_is<char>, Policy1_is<int> > B5; 

     // similar static asserts work for B1 ... B4  
     BOOST_STATIC_ASSERT((std::is_same<B5::P1, int >::value)); 
     BOOST_STATIC_ASSERT((std::is_same<B5::P2, char>::value)); 

     return 0; 
} 

爲了避免非常微妙的ODR違反基於策略的設計(一個解釋看這個老post通過Alexandrescu的),我希望能夠在指定的模板參數應用CRTP模式:

int main() 
    { 
      // ERROR: this code does NOT compile! 
      struct CuriousBreadSlicer 
      : 
        BreadSlicer< Policy1_is<CuriousBreadSlicer> > 
      {}; 

      typedef CuriousBreadSlicer B6; 

      BOOST_STATIC_ASSERT((std::is_same<B6::P1, CuriousBreadSlicer>::value)); 
      BOOST_STATIC_ASSERT((std::is_same<B6::P2, DefaultPolicy2 >::value)); 

      return 0; 
    } 

然而,Boost.Parameter執行上述編譯失敗,因爲一些內部static_assert失敗等(VC10 SP1)的消息

「主:: CuriousBre adSlicer」:一個未定義類是不允許作爲 參數編譯器的內部型性狀‘__is_base_of’

問題:可以在這個靜態檢查被關閉?無論是通過宏觀或模板技巧?

至於可能的變通:

  1. 上面的代碼是功能上等同於this handwritten code。對於該代碼,CRTP模式確實有效。然而, 需要很多Boost.Parameter庫方便的 自動化的鍋爐代碼。
  2. 我可以要求CRTP參數總是在模板 參數列表中第一個,而不是將它包裝在Policy1_is類中。這解決了編譯時錯誤,但失去了重寫順序的獨立性。

所以看來我是高爾夫球員所謂的「在俱樂部之間」。哪種解決方案最好?

回答

1

一個最小的例子,沒有CRTP:

#include <boost/parameter.hpp> 
#include <boost/static_assert.hpp> 

BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy1_is) 
BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy2_is) 

typedef boost::parameter::parameters< 
      boost::parameter::optional<tag::Policy1_is>, 
      boost::parameter::optional<tag::Policy2_is> 
     > PolicySelector; 


struct foo {}; 
struct bar {}; 
struct baz; 
typedef typename PolicySelector::bind<foo, baz>::type Policies; 
boost::parameter::value_type<Policies, tag::Policy1_is, bar>::type x; // <- !!! 

所以boost::parameter::value_type要求是基於完整的類型的政策選擇,這是不是你的手寫類的情況。

我不完全確定爲什麼一個班級需要自己作爲自己的政策。如果你需要的是,也許你可以在東西完全包裹的不完全類型:

struct CuriousBreadSlicer : BreadSlicer < 
      Policy1_is<CuriousBreadSlicer *> > // <- compiles 

或者你可以使用自己的wrap_incomplete_type<>模板,爲了清楚。

當您使用該策略時,您可以檢查它是否被包裝,並將其解包。

+0

CRTP用於避免來自類型特徵的ODR。看到我的問題鏈接Alexandrescu的帖子。實質上,政策類用於強制所有專業特徵可見。你的包裝想法很好。理想情況下,它應該是一個額外的參數,默認爲'false',它將被傳遞給'boost :: parameter :: value_type ' – TemplateRex

+0

命名模板參數似乎是一個非常重要的需求進行有效的基於策略的編程。他們怎麼沒有進入標準? – AlwaysLearning