c++ - Clang 和 GCC 误推模板参数

标签 c++ templates c++11 variadic-templates

我不擅长C++,所以这可能是一个新手错误。我正在尝试制作一个异构链表类型,其中每个节点的类型和列表其余部分的类型在每个节点中都是已知的。

这是一个 SSSCE:

#include <utility>

template<typename T, typename... Rest>
struct hnode {
    T data;
    hnode<Rest...>* next;
};

template<typename T>
struct hnode<T> {
    T data;
    std::nullptr_t next;
};

template<typename T, typename... Rest>
hnode<T> hcons(T&& val, std::nullptr_t) {
    return { std::forward<T>(val), nullptr };
}

template<typename T, typename... Rest>
hnode<T, Rest...> hcons(T&& val, hnode<Rest...>& next) {
    return { std::forward<T>(val), &next };
}

int main() {
    hnode<int> three = hcons(1, nullptr);
    auto two = hcons("hi", three);
}

但是,对于该代码,GCC 给我这个错误:

test.cc: In function ‘int main()’:
test.cc:28:29: error: no matching function for call to ‘hcons(const char [3], hnode<int>&)’
 auto two = hcons("hi", three);
                             ^
test.cc:28:29: note: candidates are:
test.cc:17:10: note: template<class T, class ... Rest> hnode<T> hcons(T&&, std::nullptr_t)
 hnode<T> hcons(T&& val, std::nullptr_t) {
          ^
test.cc:17:10: note:   template argument deduction/substitution failed:
test.cc:28:29: note:   cannot convert ‘three’ (type ‘hnode<int>’) to type ‘std::nullptr_t’
 auto two = hcons("hi", three);
                             ^
test.cc:22:19: note: hnode<T, Rest ...> hcons(T&&, hnode<Rest ...>&) [with T = const char (&)[3]; Rest = {int, Rest}]
 hnode<T, Rest...> hcons(T&& val, hnode<Rest...>& next) {
                   ^
test.cc:22:19: note:   no known conversion for argument 2 from ‘hnode<int>’ to ‘hnode<int, Rest>&’

Clang 稍微简洁一些,但仍然不足以帮助我修复它:

test.cc:28:12: error: no matching function for call to 'hcons'
auto two = hcons("hi", three);
           ^~~~~
test.cc:17:10: note: candidate function [with T = char const (&)[3], Rest = <>] not viable: no known conversion from 'hnode<int>' to 'std::nullptr_t' (aka 'nullptr_t') for 2nd argument
hnode<T> hcons(T&& val, std::nullptr_t) {
         ^
test.cc:22:19: note: candidate template ignored: substitution failure [with T = char const (&)[3], Rest = <>]: too few template arguments for class template 'hnode'
hnode<T, Rest...> hcons(T&& val, hnode<Rest...>& next) {
                  ^              ~~~~~
1 error generated.

这看起来很奇怪,因为它正在推导 Rest成为<>什么时候应该清楚 <int> , 它说它是 <int>在上面的线上 no known conversion from 'hnode<int>' to 'std::nullptr_t' .我犯了什么错误?

最佳答案

虽然 galop1n 的答案是一个有效的解决方法,但它并没有解决您代码中的真正问题。

虽然我不是 C++ 语言律师或其他什么,但我在 SO 上的某个地方读到模板匹配是一个相当严格的过程。反射(reflection)您的原始代码:在您的 hcons 中函数,你要求编译器匹配 next (应该是 hnode<typename...> 类型)到 hnode<typename, typename...> .正如您可以想象的那样,这并不是真正的精确匹配。它也不符合您的 1 个参数特化,即 hnode<typename> .

为了让你的代码通过编译,你只需要改变一件事,那就是提供一个hnode的空声明。作为接受可变数量的模板参数,并将其专门用于 1 参数版本和多参数版本:

#include <utility>

// empty declaration with variable number of arguments
template<typename...>
struct hnode;

// specialization for 1 template argument
template<typename T>
struct hnode<T> {
    T data;
    std::nullptr_t next;
};

// specialization for multiple template arguments
template<typename T, typename... Rest>
struct hnode<T, Rest...> {
    T data;
    hnode<Rest...>* next;
};

template<typename T>
hnode<T> hcons(T&& val, std::nullptr_t) {
    return { std::forward<T>(val), nullptr };
}

template<typename T, typename... Rest>
hnode<T, Rest...> hcons(T&& val, hnode<Rest...>& next) {
    return { std::forward<T>(val), &next };
}

int main() {
    hnode<int> three = hcons(1, nullptr);
    auto two = hcons("hi", three);
}

现在您的 hcons函数可以匹配 hnode<typename...>确切地说,并将实例化相应的特化。 POC on ideone

众所周知,Visual Studio 的编译器在处理与模板相关的内容时有点松懈,因此这可能是它接受您的原始代码的原因。

可以在 this 中找到相关问题,结果证明是 GCC 和 Clang 中的错误,因此 VS 是正确接受您的代码的那个。

另请注意:通过删除 std::nullptr_t hnode 的第一个参数特化中的成员, 你可以节省一点内存。

关于c++ - Clang 和 GCC 误推模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21209401/

相关文章:

c++ - 当 T==void 时如何最好地解决 "void foo( const T& t = T() )"

c# - 如何根据单元格背景颜色更改 WPF DataGrid 单元格小部件背景颜色?

c++ - 使用智能指针将右值引用绑定(bind)到抽象类的替代方法

c++ - inotify 的替代方法以检测何时在文件夹下创建新文件

c++ - 如何确定 Win32 线程是否已终止?

c++ - "<?="运算符的意义

c++ - 使用数组而不是 vector 合并排序实现

java - 函数参数中的模板

c++ - 可变参数模板的声明点

c++ - 复制列表初始化和传统复制初始化有什么区别?