2017-07-07 14 views
8

Mam bibliotekę z kilkoma obiektami funkcji, które mogą akceptować tylko kilka typów w zależności od std::is_integral. Chcę, aby std::is_invocable zwrócił false, gdy warunek się nie powiedzie, ale chcę również ładny komunikat o błędzie static_assert, gdy użytkownik próbuje wywołać instancję obiektu funkcji. Oto uproszczony przykład obiektów funkcyjnych Obecnie mam:Najlepsze wyniki static_assert i std :: is_invocable

struct function 
{ 
    template<typename Iterator> 
    auto operator()(Iterator first, Iterator last) const 
     -> std::enable_if_t<std::is_integral_v< 
      typename std::iterator_traits<Iterator>::value_type 
     >> 
    { /* something */ } 
}; 

Dzięki takiej realizacji, std::is_invocable jest std::false_type jak oczekiwano, gdy warunek SFINAE nie jest spełniony, ale użytkownicy napotykają brzydkich komunikatów o błędach SFINAE gdy próbują zadzwonić obiekt funkcji z parametrami, które nie spełniają warunku SFINAE.

Aby uzyskać lepsze komunikaty o błędach, próbowałem następujące rozwiązanie zamiast:

struct function 
{ 
    template<typename Iterator> 
    auto operator()(Iterator first, Iterator last) const 
     -> void 
    { 
     static_assert(std::is_integral_v<typename std::iterator_traits<Iterator>::value_type>, 
         "function can only be called with a collection of integers"); 

     /* something */ 
    } 
}; 

Dzięki tej realizacji, użytkownicy otrzymują przyjazne komunikaty o błędach, gdy oryginalny stan SFINAE nie jest spełniony, ale std::is_invocable jest std::true_type pytany, czy function wystąpienie może obsłużyć typ, który nie spełnia std::is_integral.

Próbowałem kilka sztuczek i zmian obejmujących decltype(auto), if constexpr i innych mechanizmów, ale nie mógł dostać klasę gdzie komunikaty o błędach były ładne i gdzie std::is_invocable odpowiadała oczekiwanemu std::false_type gdy pytając czy function mógł być wywołany z niewłaściwych typów.

Czego mi tu brakuje? Czy istnieje sposób na uzyskanie odpowiednich komunikatów o błędach przyjaznych dla użytkownika?

+0

Właśnie napisałem post na blogu na ten temat: https://gracicot.github.io/tricks/2017/07/01/deleted-function-diagnostic.html –

Odpowiedz

7

Oto jeden okropny sposób. Dodaj przeciążenie:

template <typename Iterator> 
auto operator()(Iterator first, Iterator last) const 
    -> std::enable_if_t<std::is_integral_v< 
     typename std::iterator_traits<Iterator>::value_type 
    >>; 

template <typename Iterator, class... Args> 
void operator()(Iterator, Iterator, Args&&...) const = delete; // must be integral 

ta spełnia warunek is_invocable<>, ponieważ contrained szablon funkcja jest bardziej wyspecjalizowany i preferowane - więc jeśli warunek jest spełniony, funkcja jest invocable, inaczej zostaną usunięte.

to robi się trochę lepiej w przypadku błędu, ponieważ jeśli spróbujesz to nazwać, można uzyskać:

foo.cxx: In function ‘int main()’: 
foo.cxx:31:13: error: use of deleted function ‘void function::operator()(Iterator, Iterator, Args&& ...) const [with Iterator = std::__cxx11::basic_string<char>*; Args = {}]’ 
    f(&i, &i); 
      ^
foo.cxx:19:10: note: declared here 
    void operator()(Iterator, Iterator, Args&&...) const = delete; // must be integral 
      ^~~~~~~~ 

Komentarz pojawia się w komunikacie o błędzie, który jest trochę jak statycznym assert wiadomość?


Jest to właściwie jedna z motywacji Koncepcji. Z requires zamiast enable_if, otrzymujemy:

foo.cxx: In function ‘int main()’: 
foo.cxx:26:13: error: no match for call to ‘(function) (std::__cxx11::string*, std::__cxx11::string*)’ 
    f(&i, &i); 
      ^
foo.cxx:11:10: note: candidate: void function::operator()(Iterator, Iterator) const requires is_integral_v<typename std::iterator_traits<_Iter>::value_type> [with Iterator = std::__cxx11::basic_string<char>*] 
    void operator()(Iterator first, Iterator last) const 
      ^~~~~~~~ 
foo.cxx:11:10: note: constraints not satisfied 
foo.cxx:11:10: note: ‘is_integral_v<typename std::iterator_traits<_Iter>::value_type>’ evaluated to false 

To ... trochę lepiej chyba.

+0

Rozwiązania koncepcyjne są rzeczywiście nieco lepsze. Są inne miejsca, w których potrzebuję zachowania SFINAE, musiałbym sprawdzić, czy '= delete' jest wykonalne w moim przypadku, ale może to być interesująca ścieżka do zbadania. – Morwenn

+4

Znalazłem [artykuł] (https://gracicot.github.io/tricks/2017/07/01/deleted-function-diagnostic.html), który poprawia sztuczkę '= delete', umożliwiając zapewnienie dedykowanego' static_assert' message, który nie jest zły. – Morwenn

+0

@Morwenn Zastanawiam się, czy autor artykułu ma rację ... Czytam ci ** nie mogę ** stworzyć szablonu, który nie będzie miał żadnej ważnej specjalizacji i 'static_assert (! Std :: is_same_v ," wiadomość ")' złamać tę zasadę, sprawiając, że kod będzie źle sformułowany bez wymaganej diagnostyki. –

Powiązane problemy