【问题标题】:Problems with function template and type_traits函数模板和 type_traits 的问题
【发布时间】:2017-04-28 11:43:13
【问题描述】:

我有一个函数“has_holes”,它会根据掩码计算一些东西。位数由“掩码”的类型决定。因此我想使用模板。此外,我只想允许 has_holes 的实例化,它按值获取参数。所以我添加了一个类型特征“remove_all_t”,它给出了基础类型。但是当我这样做时,我无法再构建它并得到错误:

“错误:没有匹配函数调用‘has_holes(unsigned int&)” 注意:候选模板被忽略:无法推断模板参数“BASE_TYPE”

但是,如果我明确地将功能实例化称为“has_holes”,它就可以工作。 我是否搞砸了模板实例化和类型推断规则?或者我的错误在哪里?

代码如下:

#include <iostream>
#include <limits>
#include <type_traits>
#include <experimental/type_traits>


//removes everything except arrays []
template<class T> struct remove_all { typedef T type; };
template<class T> struct remove_all<T*> : remove_all<T> {};
template<class T> struct remove_all<T&> : remove_all<T> {};
template<class T> struct remove_all<T&&> : remove_all<T> {};
template<class T> struct remove_all<T const> : remove_all<T> {};
template<class T> struct remove_all<T volatile> : remove_all<T> {};
template<class T> struct remove_all<T const volatile> : remove_all<T> {};
template<class T>
using remove_all_t = typename remove_all<T>::type;

template<typename BASE_TYPE, class = typename std::enable_if_t<std::experimental::is_unsigned_v<BASE_TYPE>, BASE_TYPE>>
bool has_holes(remove_all_t<BASE_TYPE> mask){ //remove "remove_all_t<>" and it will work

    static_assert(std::numeric_limits<unsigned int>::max() > std::numeric_limits<decltype(mask) >::digits, "Base_type has to much digits max_digits=std::numeric_limits<unsigned int>::max()");
    for (unsigned int  pos{1}; pos<std::numeric_limits<decltype(mask)>::digits; ++pos ){
        ;//algorithm will be placed here, not implemented yet
    }
    return true;
}

int main()
{
    unsigned int mask = 0b00110011;
    auto result = has_holes<unsigned int>(mask); //works
    auto result2 = has_holes(mask);//error: no matching function for call to 'has_holes(unsigned int&)'|
    std::cout<<result<<" ..."<<result2<<std::endl;
    return 0;
}

最好的问候, 亨德里克

【问题讨论】:

  • 您的错误消息中应该包含更多内容,例如 candidate template denied: could't infer template argument 'BASE_TYPE'。编译器无法推断出你想要 BASE_TYPE 是什么。
  • 我将 gcc 与 Codeblocks 一起使用,其中没有显示完整的输出。我知道有一个选项可以启用 fullouput,但我还没有找到。
  • 尝试使用 clang 编译会出现以下错误: //error: no matching function for call to 'has_holes(unsigned int&)'|注意:候选模板被忽略:无法推断模板参数“BASE_TYPE”

标签: c++ templates typetraits type-deduction


【解决方案1】:

在 C++ 中,模板推导不是无限的。它将拒绝尝试反转可能的图灵完备过程。

C++ 标准将此称为非推导上下文:不会推导模板参数的上下文。

remove_all_t&lt;BASE_TYPE&gt; 引入了一个非推断的上下文。因为当您通过 remove_all_t&lt;X&gt; 之类的模板映射类型时,理论上,映射过程可能是图灵完备的。

通常,模板类型映射不会告诉 C++ 如何反转它们。该标准告诉编译器不要尝试。

删除remove_all_t&lt;BASE_TYPE&gt; 并替换为BASE_TYPE,它将被推断为一个值,始终如此。

如果您担心有人将 int&amp; 显式传递为模板类型参数,请在函数主体中添加 static_assert( std::is_same&lt;remove_all_t&lt;BASE_TYPE&gt;, BASE_TYPE&gt;::value, "values only");

【讨论】:

  • 您的解决方法很简单并且有效:) 但我不明白您描述的问题。为什么我的映射是一个坏主意,而 stl 用它的所有类型来做它?你是什​​么意思wirh“->反转
  • 这不是一个坏主意。 std 这样做是因为这就是您如何在 C++ 中的类型特征中将一种类型转换为另一种类型。这些映射不会告诉编译器如何反转它们。您要求BASE_TYPE 类型,使得remove_all_t&lt;BASE_TYPE&gt; 与函数参数匹配。这需要反转 remove_all_t 类型映射。在一般情况下,用图灵完备语言(包括模板元编程)反转函数是不可能的。所以 C++ 声明编译器甚至不应该尝试。他们在非推导上下文中使用模板参数调用它。
  • 感谢您的解释。在阅读 Scott Meyers “有效的现代 C++ 第 1 项”之后,我意识到我根本不需要“remove_all”type_trait。 Mask 的类型将是一个左值和引用,const 和 volatile 无论如何都会被忽略。但是,static_assert 对于“邪恶的用户”来说是必需的。
  • 我认为 static_assert 也不是必需的,因为 enable_if 语句应该拒绝任何引用和指针类型。
【解决方案2】:

由于您关心位数,因此在计算位数时只需进行类型归约即可。

template<typename BASE_TYPE,
         class = typename std::enable_if_t<std::experimental::is_unsigned_v<remove_all_t<BASE_TYPE> > > 
        >
bool has_holes(BASE_TYPE mask)
{
    using reduced_type = remove_all_t<BASE_TYPE>;
    static_assert(std::numeric_limits<unsigned int>::max() > std::numeric_limits<reduced_type>::digits,
                  "Base_type has to many digits max_digits=std::numeric_limits<unsigned int>::max()");
    for (unsigned int pos{1};    // really start at 1 (nor 0)?
         pos < std::numeric_limits<reduced_type>::digits;
         ++pos )
    {
        //algorithm will be placed here, not implemented yet
    }
    return true;
}

虽然你大概不想接受BASE_TYPE*(所以必须修改它)。

【讨论】:

  • 关于 for 循环:start 1 可能看起来很奇怪,但在这个地方对于算法来说是正确的。
【解决方案3】:

模板参数推导尝试,如[temp.deduct.call]/p1中所述 与P = typename remove_all&lt;BASE_TYPE&gt;::typeA = unsigned int。最终,BASE_TYPE 不能被演绎(也不能被默认),这就变成了对 [temp.deduct.type]/p2 的不完全演绎。

作为旁注,我将使用一个简单的解决方法(因为您已经有了其他静态断言)

template<typename BASE_TYPE, class = typename std::enable_if_t<std::experimental::is_unsigned_v<BASE_TYPE>, BASE_TYPE>, 
         typename RBASE_TYPE = remove_all_t<BASE_TYPE>>
                  ^^^^^^^^^^
bool has_holes(BASE_TYPE mask){

    // Use RBASE_TYPE here

    return true;
}

Simplified example

【讨论】:

    猜你喜欢
    • 2019-07-19
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 2018-09-08
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 1970-01-01
    相关资源
    最近更新 更多