2015-10-27 10 views
10

Ho usato espressioni SFINAE per verificare se un tipo supporta operator<<C'è un modo per scrivere un test SFINAE di "per-mangiabilità" di un tipo?

namespace details 
{ 
    template<typename T> 
    struct sfinae_true : std::true_type 
    { 
    }; 

    template<typename T> 
    sfinae_true<decltype (std::declval<std::ostream &>() << std::declval<T const &>())> test_for_ostream (int); 

    template<typename T> 
    std::false_type test_for_ostream (long); 
} 

template<typename T> 
struct supports_ostream : decltype (details::test_for_ostream<T> (0)) 
{ 
}; 

Quello che vorrei provare è se questo tipo T può essere iterato su come questo

for (auto && v : vs) {} // vs is T const & 

Il dilemma è che questa è un'affermazione e non un'espressione che lo rende incompatibile con decltype

Stavo pensando di usare lambda per convertire una frase in un'espressa ione come questo

auto x = []() { for (auto && v : vs) {}; return 0; }(); // vs is T const & 

Tuttavia decltype delle espressioni che contengono lambda sembra essere esplicitamente proibito:

// Won't compile in clang, gcc nor VC++ 
using x_t = decltype ([]() { for (auto && v : vs) {}; return 0; }()); // vs is T const & 

modo che squalifica per l'uso in una funzione di test come questo:

namespace details 
{ 
    template<typename T> 
    sfinae_true<decltype (
    []() { for (auto && v : std::declval<T const &>()) ; }() 
    )> test_for_container (int); 
    // Won't work because lambdas aren't allowed in unevaluated contexts 

    template<typename T> 
    std::false_type test_for_container (long); 
} 

template<typename T> 
struct is_container : decltype (details::test_for_container<T> (0)) 
{ 
}; 

Così Ho esaurito le idee, quindi ho pensato che forse qualcuno @Stackoverflow può inventare qualcosa di interessante.

PS.

Posso capire un po 'perché decltype ([]() {}) non è consentito ma decltype ([]() {}()) deve essere sempre definito, ad esempio void.

+1

FWIW, la mia risposta [qui] (https://stackoverflow.com/questions/32293860/simulating-the-range-based-for-loops-begin-end-behavior) è amichevole con SFINAE, ovvero controlla che la validità delle chiamate funzioni. – Columbo

+0

Ciao, grazie. Quello funzionerebbe. La mia stessa pugnalata sembrava un po 'simile ma mancavano alcuni casi. – FuleSnabel

risposta

5

Per la maggior parte dei casi la seguente caratteristica dovrebbe essere sufficiente:

#include <type_traits> 
#include <utility> 
#include <iterator> 

namespace detail 
{ 
    using std::begin; 
    using std::end; 

    template <typename T> 
    auto is_range_based_iterable(...) 
     -> std::false_type; 

    template <typename T 
      , typename I = typename std::decay<decltype(std::declval<T>().begin())>::type> 
    auto is_range_based_iterable(int) 
     -> decltype(std::declval<T>().begin() 
        , std::declval<T>().end() 
        , ++std::declval<I&>() 
        , void() 
        , std::integral_constant<bool, 
         std::is_convertible<decltype(std::declval<I&>() != std::declval<I&>()), bool>::value 
        && !std::is_void<decltype(*std::declval<I&>())>::value 
        && std::is_copy_constructible<I>::value 
        >{}); 

    template <typename T 
      , typename I = typename std::decay<decltype(begin(std::declval<T>()))>::type> 
    auto is_range_based_iterable(char) 
     -> decltype(begin(std::declval<T>()) 
        , end(std::declval<T>()) 
        , ++std::declval<I&>() 
        , void() 
        , std::integral_constant<bool, 
         std::is_convertible<decltype(std::declval<I&>() != std::declval<I&>()), bool>::value 
        && !std::is_void<decltype(*std::declval<I&>())>::value 
        && std::is_copy_constructible<I>::value 
        >{}); 
} 

template <typename T> 
struct is_range_based_iterable : decltype(detail::is_range_based_iterable<T>(0)) {}; 

prova:

#include <vector> 
#include <array> 

int main() 
{ 
    static_assert(is_range_based_iterable<std::vector<int>>::value, "!"); 
    static_assert(is_range_based_iterable<std::array<int, 5>>::value, "!"); 
    static_assert(is_range_based_iterable<int(&)[5]>::value, "!"); 
} 

DEMO

+2

Non è necessario testare per i membri '.begin()' /'.end() ', la ricerca ADL-aware di' begin() '/' end() 'con le versioni' std' in scope gestisce bene per te. –

+0

Ciao. Questo risponde alle domande. Penso che stavo segretamente desiderando che in qualche modo usassi l'istruzione 'for' per testarla. – FuleSnabel

+1

@LucDanton No. Posso immaginare me stesso [un contenitore] (http://coliru.stacked-crooked.com/a/2a13665c7c84235d) che è iterabile tramite le funzioni membro 'begin()'/'end()', e di chi namespace definisce 'begin()' e 'end()' che, se trovati tramite ADL, non ti permettono di enumerare gli elementi del contenitore –

Problemi correlati