2013-04-16 37 views
6

爲了使情況區別使用SFINAE Tt參數,我想知道如果語句檢查類型被聲明爲一元類型系統(用於SFINAE)

QVariant::fromValue(t); 

和/或

QVariant::value<T>(); 

編譯。如果一個人編譯,另一個人也會這樣做,除非你破解元類型系統。當且僅當T已被聲明使用Q_DECLARE_METATYPE(T)時,它們編譯。

非常簡單的用法示例,其中只需打印一個值的類型就可以打印一個變體包裝的等價物,當且僅當被元類型系統支持時(我不需要這個,但是這顯示了在一個小例子問題):

template<class T> // enable if T NOT registered in the Qt meta type system 
void print(const T &t) { 
    qDebug() << t; 
} 

template<class T> // enable if T registered in the Qt meta type system 
void print(const T &t) { 
    qDebug() << QVariant::fromValue<T>(); 
} 

我知道幾個(但類似的)可能性,這樣做,但他們引進一些輔助結構,複雜enable_if等。現在,我知道有QTypeInfo我猜它, ,已經提供了類似於「在Qt元類型系統中聲明的」類型特徵。然而,這個類沒有記錄,因此它不被建議用於長期和高效的代碼,因爲它可能會在Qt版本之間變化。

如果QVariant支持類型T,是否有一種非常簡單的方法(比使用「checker」+ enable_if更簡單)檢查SFINAE專業化?

請注意,在不同的Qt版本(Qt4和Qt5可能使用不同的QTypeInfo定義)之間,解決方案仍然應該是便攜式。但是,我使用C++ 11,因此我有權訪問std::enable_if

「不可移植」的方式是使用內部定義QMetaTypeId2<T>::Definedenable_if(它是一個枚舉值定義爲0或1)。因此,工作的解決辦法是:

template<class T> 
typename std::enable_if<!QMetaTypeId2<T>::Defined>::type 
print(const T &t) { 
    qDebug() << t; 
} 

template<class T> 
typename std::enable_if<QMetaTypeId2<T>::Defined>::type 
print(const T &t) { 
    qDebug() << QVariant::fromValue<T>(); 
} 

然而,由於QMetaTypeId2未記錄,只有內部的東西,它不應該出現在客戶端代碼。

+0

糾正我,如果我錯了:你想知道在**編譯時**做了一些類型在**運行時註冊在Qt-meta系統** ???如果是 - 這是無稽之談 – borisbn

+0

不,我的意思是「聲明」,即存在一個「Q_DECLARE_METATYPE(T)」。對不起,我會糾正它。 – leemes

+0

我能找到的最接近的是'qMetaTypeId ()',但是如果'T'未註冊會導致編譯器錯誤 - 不太好。 – cmannett85

回答

1

你應該聲明一個能夠幫助你的包裝。我能想到的最好的是有幾個定義,基於Qt的version

template<typename T> 
struct is_registered 
{ 
    enum 
    { 
     value = 
#if QT_VERSION >= 0x050000 // Qt 5.0.0 
      QMetaTypeId2<T>::Defined 
#elif QT_VERSION >= 0x040000 // Qt 4.0.0 
      QMetaTypeId2<T>::Defined 
#endif 
    }; 
}; 

這不是審美的,但畢竟是functionnal,並在你的代碼,你可以使用is_registered<T>::value,而不必擔心版本Qt的。另外,我目前沒有Qt5,所以我不能告訴你QMetaTypeId2<T>::Defined是否正確(儘管我認爲是)。


無法使用qMetaTypeId<T>()來檢查類型是否已註冊。實際上,表達式qMetaTypeId<T>()總是有效的,無論類型如何。如果它沒有註冊,函數的正文將不會編譯(更確切地說:在Qt 4和5中(目前),qMetaTypeId<T>()只調用另一個函數,如果該類型未註冊,則不會編譯。 ,你不能使用SFINAE來測試它。結果,代碼列表在他的(現在刪除的)答案中給出了will not work as expected

的代碼是:

struct _test_is_declared_metatype 
{ 
    template<class T> 
    static auto test(T* t) -> decltype(qMetaTypeId<T>(), std::true_type()); 

    static std::false_type test(...); 
}; 

template<class T> 
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0)) 
{ 
}; 

爲什麼這是不行的?其意圖是因爲在非註冊類型上調用qMetaTypeId<T>()會導致編譯錯誤,「當類型未註冊時,SFINAE將排除第一個函數」。這裏的問題是qMetaTypeId<T>()始終是一個有效的表達式,所以qMetaTypeId<T>(), std::true_type()也是如此,並且decltype(qMetaTypeId<T>(), std::true_type())已完美定義(值爲std::true_type)。
這是因爲qMetaTypeId<T>()編譯錯誤的體的功能,而不是在它的原型(順便莖如果decltype函數聲明和正確調用代碼將只編譯,即沒有一個模板參數例如非模板功能)。
因此,因爲這個test()的超載比可變參數更具體,所以它總是會被選中,因此它總是會「返回」該類型被註冊;您可以在以下測試代碼中看到它:

// ---------------------------------------------------------- 
// qmetatype.h simplification ------------------------------- 
// ---------------------------------------------------------- 

template<typename T> 
struct metatype 
{ 
enum { defined = 0 }; 
}; 

template<typename T> 
struct metatype2 
{ 
enum { defined = metatype<T>::defined }; 
static inline int id() { return metatype<T>::id(); } 
}; 

template <typename T> 
inline int metatypeId(
    T * /* dummy */ = 0 
) 
{ 
    return metatype2<T>::id(); 
} 

#define register_meta_type(_type_) \ 
template<>       \ 
struct metatype<_type_>   \ 
{         \ 
    enum { defined = 1 };    \ 
    static int id()      \ 
    {         \ 
    /* Run-time registration in Qt */ \ 
    return __COUNTER__;    \ 
    };         \ 
}; 



// ---------------------------------------------------------- 
// ---------------------------------------------------------- 
// ---------------------------------------------------------- 

class TestA {}; 
register_meta_type(TestA) 

class TestB {}; 

class TestC {}; 
register_meta_type(TestC) 

class TestD {}; 


#include <type_traits> 

struct _test_is_declared_metatype 
{ 
/* 
    metatypeId<T>() is always a valid expression. So this overload is 
    always taken 
*/ 
    template<class T> 
    static auto test(T* t) -> decltype(metatypeId<T>(), std::true_type()); 

    static std::false_type test(...); 
}; 

template<class T> 
struct is_declared_metatype : decltype(_test_is_declared_metatype::test<T>(0)) 
{ 
}; 

#include <iostream> 
#define PRINT_DEF(_type_) std::cout << #_type_ << " registered ? " << is_declared_metatype<_type_>::value << "\n"; 
int main() 
{ 
std::cout << std::boolalpha; 
PRINT_DEF(TestA); 
PRINT_DEF(TestB); 
PRINT_DEF(TestC); 
PRINT_DEF(TestD); 
} 

您可能想要read more about SFINAE。另外,你可以read qmetatype.h here

+0

謝謝。這對兩者都是正確的,那不是重點。重點是:它沒有記錄,這意味着未來它可能會比記錄的東西更改。另一方面,例如'qMetaTypeId ()'被記錄並提供了一張支票。如果'T'沒有註冊,它將無法編譯。這意味着需要一個SFINAE技巧來將「可編譯性」映射爲布爾編譯時間值。 – leemes

+0

我查看了源代碼,對於我來說qMetaTypeId()不能與SFINAE一起使用。另外,Qt 4.x中的非文檔代碼將會永遠出現在這些版本中,所以我認爲使用預處理器是可以的。 – Synxis

+0

爲什麼你認爲'qMetaTypeId()'不能和SFINAE一起使用?我是SFINAE的新手,所以也許我忽略了一些細節。 – leemes