简体   繁体   English

Clang和GCC误导模板参数

[英]Clang & GCC misdeducing template parameters

I am not good at C++, so this is probably a novice error. 我不擅长C ++,因此这可能是新手错误。 I am trying to make a heterogeneous linked list type where the type of each node and those of the rest of the list are known in each node. 我正在尝试创建一个异构链表类型,其中每个节点的类型以及其余列表的类型在每个节点中都是已知的。

Here is an SSSCE: 这是一个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);
}

However, GCC gives me this error for that code: 但是,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 is slightly more concise but still not helpful enough for me to fix it: 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.

It seems weird because it's deducing Rest to be <> when it should clearly be <int> , and it says that it's <int> on the line above with no known conversion from 'hnode<int>' to 'std::nullptr_t' . 这似乎不可思议,因为它演绎Rest<>时,应明确是<int> ,它说,这是<int>上面用线no known conversion from 'hnode<int>' to 'std::nullptr_t' What error did I make? 我犯了什么错误?

While galop1n's answer is a valid work-around, it doesn't address the real problem in your code. 尽管galop1n的答案是有效的解决方法,但它不能解决代码中的实际问题。

Although I'm not a C++ language lawyer or anything, I've read somewhere on SO that template matching is a rather strict process. 尽管我不是C ++语言律师或其他任何人,但是我在SO的某处读到模板匹配是一个相当严格的过程。 To reflect on your original code: in your hcons function, you are asking the compiler to match next (which is ought to be of type hnode<typename...> ) to hnode<typename, typename...> . 反思原始代码:在hcons函数中,您要求编译器next其匹配(应该是hnode<typename...>类型)与hnode<typename, typename...> As you can imagine this isn't really an exact match. 可以想象,这并不是完全匹配。 It also doesn't match your 1 argument specialization, namely hnode<typename> . 它也与您的1个参数专业化(即hnode<typename>不匹配。

To make your code compile, you only need to change 1 thing, and that is to provide an empty declaration of hnode as accepting a variable number of template arguments, and specialize it for both the 1 parameter version, and the multiple parameter version: 为了使您的代码编译,您只需要更改1件事,那就是提供一个空的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);
}

Now your hcons function can match hnode<typename...> exactly, and will instantiate the corresponding specialization. 现在,您的hcons函数可以完全匹配hnode<typename...> ,并将实例化相应的专业化。 POC on ideone 乙二酮的POC

Visual Studio's compiler is known to be a little lax when it comes to template-related stuff, so that might be the reason why it accepts your original code. 众所周知,Visual Studio的编译器在与模板相关的内容上有些松懈,所以这可能就是为什么它接受原始代码的原因。

As can be found in this related question, it turns out to be a bug in GCC and Clang, so VS is the one that is correctly accepting your code. 这个相关问题中可以发现,事实证明这是GCC和Clang中的错误,因此VS是正确接受您代码的代码。

Also as a side note: by removing the std::nullptr_t member in your 1 argument specialization of hnode , you can save a little bit of memory. 另外,作为一个侧面说明:通过删除std::nullptr_t在你的论点1专业化成员hnode ,可以节省一点点内存。

Not sure why but as hnode has at least one template parameter, this works : 不知道为什么,但是由于hnode具有至少一个模板参数,因此可以:

#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>
hnode<T> hcons(T&& val, std::nullptr_t) {
    return { std::forward<T>(val), nullptr };
}

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

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

On your version, once it fails to find a best match, it is normal the compiler list all the candidate. 在您的版本上,一旦找不到最佳匹配,通常编译器会列出所有候选对象。 The real question still remains, why clang is wrong on the template parameter pack deduction… 真正的问题仍然存在,为什么clang在模板参数包推导上是错误的...

Visual studio 2013 succeed on both version ( with and without S ), still it fails on the forward of a const char(*)[3] i replace with a int for the test… Visual Studio 2013在两个版本(带有和不带有S)上都成功,但是在const char(*)[3]的前面却失败了,我用int代替了测试…

Clang or Visual, who is right, i do not know, but it may be a good idea to fill a bug report here. 我不知道是Clang还是Visual,是对的,但在此处填写错误报告可能是一个好主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM