В чем разница между 2 версиями std :: enable_if - PullRequest
2 голосов
/ 26 апреля 2019

Я не понимаю, почему первая ( хорошая ) версия кода компилируется, а вторая -

Я прочитал это , это , это и, конечно, это , но я до сих пор не понимаю, почему для одной версии он компилируется, а для другого - нет. Если бы кто-то мог объяснить это (например, для пустышек), я был бы очень признателен.

ХОРОШО * версия

template <typename As, typename std::enable_if<
std::is_arithmetic<As>::value, As>::type* = nullptr   > 
As getStringAs(const std::string& arg_name)
{
    std::istringstream istr(arg_name);
    As val;
    istr >> val;
    if (istr.fail())
        throw std::invalid_argument(arg_name);
    return val;
}

ПЛОХО версия

template <typename As, typename std::enable_if_t<
std::is_arithmetic<As>::value, As> = 0   > 
As getStringAs(const std::string& arg_name)
{
    std::istringstream istr(arg_name);
    As val;
    istr >> val;
    if (istr.fail())
        throw std::invalid_argument(arg_name);
    return val;
}

Использование по назначению:

int main()
{
   return getStringAs<float>("2.f");
}

Большое спасибо!

Ответы [ 3 ]

2 голосов
/ 26 апреля 2019

std::enable_if_t<std::is_arithmetic<As>::value, As> заменяет As при условии, что условие истинно. Причина, по которой выдается ошибка, в том, что у вас не может быть нетипизированного параметра шаблона типа с плавающей запятой. В этом случае вы, похоже, не используете параметр шаблона ни по какой причине, кроме SFINAE, поэтому вы можете заменить второй As на int, и он должен скомпилироваться.

std::enable_if_t<std::is_arithmetic<As>::value, int> = 0
0 голосов
/ 26 апреля 2019

std::enable_if является типом, и я могу объявить переменные с ним:

std::enable_if<true, int> myVar;

Вы также можете написать:

std::enable_if<true, int> myVar2{};
std::enable_if<true, int> myVar3 = {};

У него нет конструктора, который принимаетцелое число, так что это не скомпилируется:

//Error - no way to convert 0 to std::enable_if<true, int>
std::enable_if<true, int> myVar = 0; 

// Error - no way to convert nullptr to std::enable_if<true, int>
std::enable_if<true, int> myVar = nullptr; 

Таким же образом, typename std::enable_if<true, int>::type* является указателем (в частности, int*).Ему можно присвоить 0, и ему также можно присвоить nullptr:

// This works, because you can assign 0 to a pointer
typename std::enable_if<true, int>::type* myPtr = 0; 
// This works, because you can assign nullptr to a pointer
typename std::enable_if<true, int>::type* myPtr = nullptr; 

Как работает enable_if. enable_if построен нахак, при котором при определенных обстоятельствах компилятор просто игнорирует экземпляр шаблонной функции, если он не скомпилируется.(NB: если объявление компилируется, а тело нет, компилятор не может это игнорировать).

Допустим, у нас есть две версии функции, и вы хотите переключаться между ними на основе некоторого условия:

// This version gets called if T::value is true, because it'll fail to compile otherwise
template<class T, typename std::enable_if<T::value>::type* = nullptr>
void foo(){
    std::cout << "T::value is true\n";
}

// This version gets called if T::value is false, because it'll fail to compile otherwise
template<class T, typename std::enable_if<not T::value>::type* = nullptr>
void foo(){
    std::cout << "T::value is false\n"; 
}

Если у вас есть два класса, оба с constexpr valueчлен, он будет вызывать правильную версию функции:

class A{
    public:
    constexpr static bool value = true;
};
class B {
    public:
    constexpr static bool value = false;
};
int main() {
    foo<A>(); // Prints T::value is true
    foo<B>(); // Prints T::value is false
}
0 голосов
/ 26 апреля 2019

Вы забыли звездочку, и у вас есть ненужные typename:

template <typename As, /*typename*/ std::enable_if_t<
std::is_arithmetic<As>::value, As>* = 0   > 
                         // here  ^
...