2016-02-26 113 views
2

我實現了std::experimental::is_detected基於on this article on cppreference.com(部分代碼在+ working repro之下)。Std :: experimental ::的奇怪MSVC行爲is_detected

它適用於G ++和Clang ++,但會導致MSVC出現奇怪的錯誤行爲:is_detected似乎總是bool_constant<true>

在這裏你可以正確的結果使用gcc 5.x看到: @ideone.com

但隨着MSVC 19(隨VS2015)測試總是成功:

Z:\>cl /EHsc test.cxx 
.... 
Z:\>test 
true, true 

所以,這是在一個已知的bug編譯器?它是否與表達式SFINAE沒有正確實現?有什麼解決方法可以用來完成這項工作嗎?

謝謝!


這裏是再現錯誤的代碼的一部分(I省略了接口的其餘部分除了is_detected提高易讀性):

#include <iostream> 

// void_t: void type alias 
template<typename...> 
using void_t = void; 
// 

namespace internal 
{ 
    // Fallback case 
    template< typename D, 
       typename Void, 
       template<typename...> class Check, 
       typename... Args 
      > 
    struct detect_impl 
    { 
     using value_t = std::false_type; 
     using type = D; 
    }; 


    // Check succeeded 
    template< typename D, 
       template<typename...> class Check, 
       typename... Args 
      > 
    struct detect_impl 
     < D, void_t< Check<Args...> >, Check, Args... > 
    { 
     using value_t = std::true_type; 
     using type = Check<Args...>; 
    }; 
} 

// Type representing a missing type. 
struct nonesuch 
{ 
    nonesuch() = delete; 
    ~nonesuch() = delete; 
    nonesuch(nonesuch const&) = delete; 
    void operator=(nonesuch const&) = delete; 
}; 


template< template<typename...> class Check, 
      typename... Args 
     > 
using is_detected = typename internal::detect_impl< nonesuch, void, Check, Args... >::value_t; 



// Our test 
template< typename T > 
using is_addable_impl = decltype(std::declval<T>() + std::declval<T>()); 

template< typename T > 
using is_addable = is_detected<is_addable_impl, T>; 


auto main(int argc, const char* arv[]) 
    -> int 
{ 
    std::cout << std::boolalpha 
       << is_addable<int>::value << ", " 
       << is_addable<nonesuch>::value << std::endl; 
} 

編輯:奇怪的是,直接使用void_t成語在MSVC上有效:

#include <iostream> 
#include <type_traits> 

struct X {}; 

template< typename T, typename = void > 
struct is_addable 
    : std::false_type 
{}; 

template< typename T > 
struct is_addable <T, std::void_t<decltype(std::declval<T>() + std::declval<T>())>> 
    : std::true_type 
{}; 

int main() 
{ 
    std::cout << std::boolalpha 
       << is_addable<int>::value << ", " 
       << is_addable<X>::value 
       << std::endl; 
} 

輸出:

Z:\>cl /EHsc test.cxx 
.... 
Z:\>test.exe 
true, false 
+2

如果你正在嘗試VS2015的基礎版本,至少嘗試更新1.已經添加了部分表達式SFINAE支持。 – chris

+0

@chris不幸的是,我已經在使用最新的CTP版本。 – nshct

回答

3

這裏的出現與最近MSVC的工作解決方法(在Visual C++ 19.00.23720.0測試):

#include <type_traits> 

template <typename...> 
using void_t = void; 

namespace internal 
{ 
    template <typename V, typename D> 
    struct detect_impl 
    { 
     using value_t = V; 
     using type = D; 
    }; 

    template <typename D, template <typename...> class Check, typename... Args> 
    auto detect_check(char) 
     -> detect_impl<std::false_type, D>; 

    template <typename D, template <typename...> class Check, typename... Args> 
    auto detect_check(int) 
     -> decltype(void_t<Check<Args...>>(), 
        detect_impl<std::true_type, Check<Args...>>{}); 

    template <typename D, typename Void, template <typename...> class Check, typename... Args> 
    struct detect : decltype(detect_check<D, Check, Args...>(0)) {}; 
} 

struct nonesuch 
{ 
    nonesuch() = delete; 
    ~nonesuch() = delete; 
    nonesuch(nonesuch const&) = delete; 
    void operator=(nonesuch const&) = delete; 
}; 

template <template<typename...> class Check, typename... Args> 
using is_detected = typename internal::detect<nonesuch, void, Check, Args...>::value_t; 

(僞void參數未使用的,現在,它的存在只是爲了保持的休息執行完整)。

+0

非常感謝!這工作非常好。 – nshct

4

您需要一個C++ 11編譯器來編譯上述代碼,並且MSVC 2015不是C++ 11編譯器。

您正在運行的C++ 11合規性中的特定缺陷被微軟稱爲「表達式SFINAE」。留意它被修復。基本上,decltype不能用於SFINAE。用俗語說,SFINAE是用於選擇模板函數或類的重載的技術。

通常沒有解決方法。

+2

我會說這不是一個符合標準的編譯器。它確實支持大多數C++ 11/14功能, – NathanOliver

+0

(查看我的編輯)那麼爲什麼直接應用void_t慣用法可行,但使用detect_impl的間接方式卻不行? – nshct

+0

@Naschkatze也許吧。它們在'decltype'下編譯器的行爲往往是片狀和脆弱的(而不是「顯然不工作」)。有時候我可以開始工作,但是不允許他們訪問他們的來源,我無法預測它。 – Yakk