std :: enable_if的2个版本之间有什么区别

时间:2019-04-25 22:38:03

标签: c++ c++11 templates c++14

我不明白为什么第一个()代码可以编译,但是第二个却不能

我已经阅读了thisthisthis,当然还有this,但我仍然不明白为什么要为一个版本编译而为另一个版本编译没有。如果有人可以解释一下(例如假人),我将不胜感激。

良好版本

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 个答案:

答案 0 :(得分:2)

std::enable_if_t<std::is_arithmetic<As>::value, As>替换As并假定条件为真。发出错误的原因是因为您不能具有浮点类型的非类型模板参数。在这种情况下,除了SFINAE之外,您出于某种原因似乎都没有使用template参数,因此您可以将第二个As替换为int并进行编译。

std::enable_if_t<std::is_arithmetic<As>::value, int> = 0

答案 1 :(得分:0)

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建立在一个hack上,在某些情况下,如果编译器无法编译模板化函数的实例,编译器只会忽略该实例。 (注意:如果声明可以编译,但是主体不能编译,则编译器不能忽略该声明)。

假设我们有一个函数的两个版本,您希望根据某些条件在两个版本之间进行切换:

// 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
}

答案 2 :(得分:-1)

您忘记了一个星号,并且不需要typename

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