enable_if vs static_assert wielki pojedynek, zobacz kto zwycięży, masakra!

enable_if vs static_assert
enable_if
0%
0% [0]
static_assert
33%
33% [1]
zamiennik enable_if
0%
0% [0]
inne
67%
67% [2]
Odpowiedz Nowy wątek
2018-05-15 14:23
0

No więc tak, w offline'owej ankiecie wyszło mi 2:2, dwie osoby za enable_if, dwie za static_assert, także dlaczego by nie zapytać forum ;)

Problem i uproszczony przykład, mam klasę, która przechowuje wartość generyczną. Dla jednego, jedynego typu chce uniemożliwić zawołanie jednej metody. I mogę to zrobić na przynajmniej dwa sposoby, każda ma swoje wady i zalet.

enable_if

template<typename T>
struct Holder
{
  T getValue() const { return m_value;}
 
  template<typename U = T, typename = typename std::enable_if<!std::is_same<U, bool>::value>::type>
  const T& getRefValue() const { return m_value; }
 
  T m_value;
};

static_assert

template<typename T>
struct Holder
{
  T getValue() const { return m_value;}
 
  const T& getRefValue() const
  {
     static_assert(!std::is_same<T, bool>::value, "Calling getRefValue for bool is not allowed"); 
     return m_value;
  }
 
  T m_value;
};

Dzięki enable_if jeśli mamy dobry edytor dostaniemy błąd już na etapie pisania kodu. Ale za to dzięki static_assert będziemy mieli czytelniejszy błąd kompilacji jeśli już ktoś niepoprawnie metody użyje. Pojawił się też argument że static_assert jakiś taki czytelniejszy. A co o tym myśli forum 4p? Ja tam wolę enable_if, vim take rzeczy ogarnia bez problemu.

I jeszcze bonusowe rozwiązanie, zamiast enable_if można by zmajstrować zamiennik z którym już każdy edytor powinien sobie poradzić:

template<typename T, typename TSelf>
struct ValueRefAccess
{
  const T& getRefValue() { return static_cast<const TSelf*>(this)->m_value; }
};
 
template<typename T, typename TSelf>
struct ValueRefAccess <bool, TSelf>
{
};
 
template<typename T>
struct Holder : ValueRefAccess<T, Holder<T>>
{
  T getValue() const { return m_value;}
 
  T m_value;
};
 
edytowany 1x, ostatnio: kq, 2018-05-15 14:27

Pozostało 580 znaków

2018-05-15 14:27
kq
2

A co z tag dispatchingiem (najczystszym rozwiązaniem do C++14) i constexpr if?

Jak dla mnie każde z rozwiązań należy rozpatrywać przez pryzmat jego charakterystyk, zamiast stwierdzać, że jedno jest lepsze od drugiego (choć tag dispatch > enable_if).

enable if/tag dispatch pozwala na sfinae
static_assert daje czytelne komunikaty błędów


Pozostało 580 znaków

2018-05-15 14:36
3

Kilka uwag ode mnie:
1) enable_if i static_assert nie działają tak samo, choć czasem działają podobnie:
1.1) konstrukcje te nie są sprawdzane w tym samym czasie,
1.2) użycie enable_if wyrzuca nam funkcje z overload set co umożliwia wykonanie kolejnej we wspomnianym overload set funkcji (konstrukcja znana jako SFINAE). static_assert zwyczajnie zatrzyma kompilacje.
2) C++17 ma constexpr if, które w wielu przypadkach będzie ładniejsze / czytelniejsze zarówno od static_assert jak i enable_if.


edytowany 1x, ostatnio: pingwindyktator, 2018-05-15 14:37
No i nie odpowiedziałeś na pytanie :/ - several 2018-05-15 14:47
Nie odpowiedziałem, bo enable_if i static_assert robią co innego. - pingwindyktator 2018-05-15 14:47
Tak, ja konkretnie zapytałem które z nich nada się lepiej do konkretnego problemu. Bądź zapytałem o alternatywę. - several 2018-05-15 14:51

Pozostało 580 znaków

2018-05-15 14:50
0

Ja wiem Panowie, że enable_if i static_assert to nie to samo, że w innym czasie ewaluowane itd. Porównuję te dwie techniki jako rozwiązanie konkretnego problemu - całkowite wycięcie jednej metody dla jednego typu. Ewentualnie szukam lepszego sposobu ;)

Tag dispatch używałem do wybierania którejś z metod, a consteprx if do zmiany zachowania metody w zależności od typu, chętnie zobaczyłbym przykłady jak koszernie je użyć żeby, powtarzam, wyciąć metode dla jednego typu.

EDIT
Mógłbym w sumie zapytać inaczej, jak wolelibyście zostać poinformowani że nie wolno wam używać metody z jakiejś biblioteki ;)

edytowany 4x, ostatnio: several, 2018-05-15 15:05

Pozostało 580 znaków

2018-05-15 18:54

Ale ... po co enable_if czy static_assert? Przecież to sprawę załatwia...

#include <iostream>
 
template<typename T>
struct Holder
{
  Holder(T a);
  T getValue() const;
  const T& getRefValue() const;
private:
  T m_value;
};
 
template<typename T>
Holder<T>::Holder(T a): m_value{a}
{
}
 
template<typename T>
T Holder<T>::getValue() const
{
    return m_value;
};
 
template<typename T>
const T& Holder<T>::getRefValue() const
{
    return m_value;
}
 
// Usunięta specjalizacja dla T ==  bool
template<>
const bool& Holder<bool>::getRefValue() const = delete;
 
int main()
{
    Holder<bool> h(true);
    auto z = h.getValue();
    // To się nie powiedzie bo specjalizacja usunięta... 
    const auto& q = h.getRefValue();
    std::cout << z << ' ' << q << '\n';
}

Każdy problem w informatyce można rozwiązać, dodając kolejny poziom pośredniości,z wyjątkiem problemu zbyt dużej liczby warstw pośredniości — David J. Wheeler
Najciemniej pod latarnią :) - several 2018-05-16 18:10

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0