2017-09-12 19 views
5

Chcę użyć if constexpr zamiast do wysyłania tagów, ale nie jestem pewien jak z niego korzystać. Przykładowy kod poniżej.jeśli constexpr zamiast wysyłki tagu

template<typename T> 
struct MyTag 
{ 
    static const int Supported = 0; 
}; 

template<> 
struct MyTag<std::uint64_t> 
{ 
    static const int Supported = 1; 
}; 

template<> 
struct MyTag<std::uint32_t> 
{ 
    static const int Supported = 1; 
}; 

class MyTest 
{ 
public: 
    template<typename T> 
    void do_something(T value) 
    { 
     // instead of doing this 
     bool supported = MyTag<T>::Supported; 

     // I want to do something like this 
     if constexpr (T == std::uint64_t) 
      supported = true; 
    } 
}; 
+2

Nie można "nazwać" '' operator == sprawie rodzajów; to jest bez znaczenia. Możesz zrobić coś podobnego z biblioteką, jak hana doładowania: 'if constexpr (hana :: type_c == hana :: type_c )'. Możesz także użyć 'std :: is_same':' jeśli constexpr (std :: is_same_v ) ' – Justin

+0

Typy nie są wartościami, nie możesz ich porównywać w ten sposób. – Rakete1111

+0

@ Rakete1111 Tak, wiem, że nie mogę porównywać ich w ten sposób, o to właśnie chodzi, o to, jak powinienem to zrobić ... i dlaczego upadłem? – 0xBADF00

Odpowiedz

3

Jednym ze sposobów jest zdefiniować predykat constexpr który sprawdza typ swojego argumentu, następnie constexpr włączyć wyniku tego predykatu.

Myślę, że ten sposób jest miły, ponieważ oddziela logikę funkcjonalną od logiki warunków wstępnych.

#include <iostream> 
#include <cstddef> 
#include <type_traits> 

class MyTest 
{ 
public: 
    template<typename T> 
    void do_something(T value) 
    { 
     // define our predicate 
     // lambdas are constexpr-if-possible in c++17 
     constexpr auto is_supported = [](auto&& x) { 
      if constexpr (std::is_same<std::decay_t<decltype(x)>, std::uint64_t>()) 
       return true; 
      else 
       return false; 
     }; 

     // use the result of the predicate   
     if constexpr (is_supported(value)) 
     { 
      std::cout << "supported\n"; 
     } 
     else 
     { 
      std::cout << "not supported\n"; 
     } 
    } 
}; 

int main() 
{ 
    auto t = MyTest(); 

    t.do_something(int(0)); 
    t.do_something(std::uint64_t(0)); 
    t.do_something(double(0)); 
    t.do_something(static_cast<unsigned long>(0)); // be careful with std::uint_xx aliases 

} 

wyniki przykład:

not supported 
supported 
not supported 
supported 

Innym sposobem wyrażenia może to być:

class MyTest 
{ 
public: 

    template<class T> 
    static constexpr bool something_possible(T&&) 
    { 
     return std::is_same<std::decay_t<T>, std::uint64_t>(); 
    } 

    template<typename T> 
    void do_something(T value) 
    { 
     // switch behaviour on result of constexpr predicate  
     if constexpr (something_possible(value)) 
     { 
      std::cout << "supported\n"; 
     } 
     else 
     { 
      std::cout << "not supported\n"; 
     } 
    } 
}; 
+0

dzięki bardzo ładnej odpowiedzi, a std :: decay jest potrzebny, jeśli T jest wskaźnikiem lub odnośnikiem, prawda? – 0xBADF00

+0

@ 0xBADF00 rozpad usuwa paski, odniesienia, itp., Dzięki czemu można porównać podstawowe typy. –