2009-09-24 103 views
3

這裏定義一個符號是我的問題:在一個標題我定義一個結構模板type_to_string,其目的是確定對應於給定類型的參數的字符串:在另一個命名空間

namespace foo { 

    template <typename T> 
    struct type_to_string 
    { 
     static const char * value; 
    }; 
} 

template <typename T> 
const char * foo::type_to_string<T>::value = "???"; 

我還定義默認值爲字符串。現在

,我想使用的宏定義新類型:

#define CREATE_ID(name)        \ 
struct name;           \ 
                 \ 
template<>           \ 
const char * foo::type_to_string<name>::value = #name; 

的問題是,我想宏在命名空間中使用,如:

namespace bar 
{ 
    CREATE_ID(baz) 
} 

這是不可能的,因爲type_to_string<T>::value必須在包含foo的命名空間中定義。

下面是編輯錯誤,我得到:

[COMEAU 4.3.10.1] error: member "foo::type_to_string<T>::value [with T=bar::baz]" 
cannot be specialized in the current scope 

[VISUAL C++ 2008] error C2888: 'const char *foo::type_to_string<T>::value' : 
symbol cannot be defined within namespace 'bar' 
    with 
    [ 
     T=bar::baz 
    ] 

奇怪的是,GCC 4.3.5(MinGW的版本)不會產生任何錯誤。

有沒有人知道這個解決方法,也許通過使用我不知道的一些查找規則(即在宏中聲明type_to_string,以便每個命名空間有它自己的版本,或類似的東西)?

回答

9

根據C++標準14.7.3/2:

一個明確的專業化應在其中的模板是一個部件,或者,對於 構件模板的命名空間聲明,在命名空間,其中的封閉類或封閉類模板是一個成員。 類模板 的成員函數,成員類或靜態數據成員的顯式特化應在類模板所屬的名稱空間中聲明。這樣的聲明也可能是一個定義。如果聲明不是一個定義,專業化可以稍後在聲明顯式專業化的名稱空間中定義,或者在名稱空間中定義,該名稱空間包含聲明顯式專業化的名稱空間。

你可以寫類似以下內容:

#define DECL_ID(name) \ 
struct name;           

#define CREATE_ID(name) \ 
template<>    \ 
const char * foo::type_to_string<name>::value = #name; 

namespace bar { namespace bar2 { 
    DECL_ID(baz) 
} } 
CREATE_ID(bar::bar2::baz) 

或者

#define CREATE_ID(ns, name)  \ 
namespace ns { struct name; } \ 
           \ 
template<>      \ 
const char * foo::type_to_string<ns::name>::value = #name; 

CREATE_ID(bar, baz) 

第三個選項是前兩種的疊加。它允許在value有不合格的名稱(如果需要):

#define DECL_ID(name) \ 
struct name;           

#define CREATE_ID(ns, name) \ 
template<>    \ 
const char * foo::type_to_string<ns::name>::value = #name; 

namespace bar { namespace bar2 { 
    DECL_ID(baz) 
} } 
CREATE_ID(bar::bar2, baz) 
+0

感謝標準中的段落,它總是很好的具備確切的要求。關於你的第二個解決方案,事實上我已經在做了:我有兩個參數,一個用於定義名稱空間,另一個用於類型。但是,我希望宏可以在另一個名稱空間中使用,以便不限制使用的名稱空間的數量。我可以用1,2,3,...參數定義CREATE_ID宏來接受幾個名稱空間名稱,但這樣會比較單調乏味。如果這是唯一的解決方案,我沒有太多的選擇... – 2009-09-24 12:09:11

+0

爲了記錄,我最終使用Boost.Preprocessor將名稱空間列表傳遞給宏。這樣,名稱可以包含在幾個嵌套的名稱空間中。 – 2010-01-11 17:44:59

1

這裏是我使用,使用升壓解決方案。預處理:

#include <boost/preprocessor/seq/for_each.hpp> 
#include <boost/preprocessor/seq/size.hpp> 
#include <boost/preprocessor/repetition/repeat.hpp> 

#define BEGIN_NS(r, _, elem) namespace elem { 
#define CLOSE_NS(z, n, _)  } 
#define APPEND_NS(r, _, elem) elem:: 

#define CREATE_ID(ns_list, name)       \ 
                 \ 
BOOST_PP_SEQ_FOR_EACH(BEGIN_NS, ~, ns_list)    \ 
    struct name;           \ 
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(ns_list), CLOSE_NS, ~) \ 
                 \ 
template<>            \ 
const char * devs::type_to_string<      \ 
    BOOST_PP_SEQ_FOR_EACH(APPEND_NS, ~, ns_list)name  \ 
>::value = #name; 

它必須在任何命名空間之外使用這樣的:

CREATE_ID((bar) (bar2), baz) 

這似乎很奇怪,我必須定義一個宏只是重複n次的字符「}」,如果任何人有一個更優雅的方式來做到這一點,隨時發表評論!