【问题标题】:Template Argument Type Deduction Fails with C++11 <type_traits>使用 C++11 <type_traits> 的模板参数类型推导失败
【发布时间】:2013-06-07 14:14:57
【问题描述】:

我正在尝试了解如何使用 C++(11) &lt;type_traits&gt;

这是我的简单测试程序

#include <type_traits>

template<class U, class S>
inline U add(typename std::enable_if<std::is_unsigned<U>::value,U>::type a,
             typename std::enable_if<std::is_signed  <S>::value,S>::type b)
{
    return a + b;
}

int main(int argc, const char * argv[], const char * envp[])
{
    unsigned int ui;
    int i;
    auto a = add(ui, i);
    return 0;
}

使用 GCC 4.8.1 编译时会出错

/home/per/f.cpp: In function ‘int main(int, const char**, const char**)’:
/home/per/f.cpp:15:23: error: no matching function for call to ‘add(unsigned int&, int&)’
     auto a = add(ui, i);
                       ^
/home/per/f.cpp:15:23: note: candidate is:
/home/per/f.cpp:5:10: note: template<class U, class S> U add(typename std::enable_if<std::is_unsigned<U>::value, U>::type, typename std::enable_if<std::is_signed<S>::value, S>::type)
 inline U add(typename std::enable_if<std::is_unsigned<U>::value,U>::type a,
          ^
/home/per/f.cpp:5:10: note:   template argument deduction/substitution failed:
/home/per/f.cpp:15:23: note:   couldn't deduce template parameter ‘U’
     auto a = add(ui, i);
                       ^

我不知道为什么 GCC 不能推导出模板参数U。任何人都知道我的代码缺少什么信息,这就是我如何在 C++11 中编写一个程序,将无符号整数类型作为第一个参数,将有符号整数类型作为第二个参数?

【问题讨论】:

  • 你不能在符合条件的:: 左边推断类型。
  • 14.8.2.5 从类型推导模板参数 [temp.deduct.type] 5 非推导上下文是: — 类型的嵌套名称说明符使用 qualiified-id 指定。
  • 感谢所有漂亮的 cmets 和答案!

标签: c++ c++11 typetraits template-argument-deduction


【解决方案1】:

typename std::enable_if&lt;std::is_unsigned&lt;U&gt;::value,U&gt;::type 不是可演绎的上下文。为了从中推断出U,编译器需要能够应用std::enable_if 的反向操作。看起来并不太难,这是真的,但那是因为你在谈论像enable_if 这样简单的事情。不可能对每个特征都要求这个,所以 C++ 只是玩得很​​酷,不会产生任何奇怪的规则例外:它一般不可推导,在这个中不可推导。

你可以这样做:

template<class U, class S,
         EnableIf<std::is_unsigned<U>, std::is_signed<S>>...>
U add(U a, S b)

或者在不正确支持该样式的编译器中,您可以添加一个额外的默认参数:

template<class U, class S>
U add(U a, S b,
      typename std::enable_if<std::is_unsigned<U>::value
          && std::is_signed<S>::value,void>::type* = nullptr)

...或者弄乱返回类型。

template<class U, class S>
typename std::enable_if<std::is_unsigned<U>::value
    && std::is_signed<S>::value,U>::type
add(U a, S b)

【讨论】:

  • 哇...这是在做什么? typename std::enable_if&lt;std::is_unsigned&lt;U&gt;::value &amp;&amp; std::is_signed&lt;S&gt;::value,void&gt;::type* = nullptr。右值指向指针?
【解决方案2】:

你没有给编译器一个推断US的机会。您可以如下重写您的函数,并在模板参数列表中移动 SFINAE 检查:

template<class U, class S,
    typename std::enable_if<std::is_unsigned<U>::value &&
                            std::is_signed  <S>::value
        >::type* = nullptr>
inline U add(U a, S b)
{
    return a + b;
}

这是live example

【讨论】:

    【解决方案3】:

    你首先必须推断类型你可以推理类型!

    应该是:

    template <typename U, typename S>
    typename std::enable_if<std::is_unsigned<U>::value &&
                            std::is_signed<S>::value>, U>::type
    add(U u, S s)
    {
        // ...
    }
    

    【讨论】:

      【解决方案4】:

      不可能从“嵌套 typedef”表达式中推导出模板参数。也就是说,可以从some_template&lt;U&gt; 推导出U,但不能从some_template&lt;U&gt;::type 推导出来。

      编译器不可能枚举some_template 的所有(无限!)实例化,并查看其中的嵌套 typedef 等于实际参数类型。

      【讨论】:

        【解决方案5】:

        试试:

        template<class U, class S>
        typename std::enable_if<std::is_unsigned<U>::value && std::is_signed<S>,U>::type  
        add(U a , S b)
        {
            return a + b;
        }
        

        【讨论】:

        • 就我个人而言,我更喜欢在返回类型上写 enable_if,因为这样可以使函数声明更加清晰。而且,此外,使模板推导工作(如你的情况)。
        • 避免使用 inline 关键字。如果 C++ 编译器认为更有效,它会内联每个函数。即使你写了 inline 与否。而且,即使你放了 inline,如果编译器认为内联该函数效率不高,它也不会内联该函数。所以,不要写 inline你不是比编译器更好的优化器。编写简单明了的代码,让编译器完成它的工作。
        猜你喜欢
        • 1970-01-01
        • 2015-10-11
        • 2021-11-01
        • 1970-01-01
        • 2021-07-01
        • 2020-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多