【问题标题】:What is the difference between the 2 versions of std::enable_if两个版本的 std::enable_if 有什么区别
【发布时间】:2019-04-25 22:38:02
【问题描述】:

我不明白为什么第一个(good)版本的代码可以编译,但第二个没有

我读过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");
}

非常感谢!

【问题讨论】:

  • std::enable_if_t&lt;cond, T&gt; 已经是typename std::enable_if&lt;cond, T&gt;::type
  • 因为你不能有浮点类型的非类型模板参数。
  • @0x499602D2 非常感谢您的回答。现在我知道用谷歌搜索什么了:)
  • @0x499602D2 考虑将其作为答案吗?目前有两个答案,但他们都没有真正回答这个问题。一个是完全错误的,一个只是解释 enable_if 是如何工作的,但没有解释为什么它不能编译。

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


【解决方案1】:

std::enable_if_t&lt;std::is_arithmetic&lt;As&gt;::value, As&gt; 替换为 As 假设条件为真。发出错误的原因是您不能有浮点类型的非类型模板参数。在这种情况下,除了 SFINAE 之外,您似乎没有出于任何原因使用模板参数,因此您可以将第二个 As 替换为 int,它应该可以编译。

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

【讨论】:

    【解决方案2】:

    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&lt;true, int&gt;::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
    }
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 使用 gcc 8.3 你的版本不能编译,除非我遗漏了什么。 godbolt.org/z/Lhe3gs
      • @DanielJour 知道为什么0 不起作用吗?据我所知,它仍然是一个有效的指针初始化器。
      猜你喜欢
      • 2020-09-30
      • 2020-10-10
      • 2015-10-20
      • 2019-07-15
      • 1970-01-01
      • 1970-01-01
      • 2018-09-04
      • 2013-08-31
      • 1970-01-01
      相关资源
      最近更新 更多