clang 6和clang 7之间的模板转换运算符区别 [英] Template conversion operator difference between clang 6 and clang 7

查看:151
本文介绍了clang 6和clang 7之间的模板转换运算符区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码使用模板转换运算符来查找通过ADL找到的函数的返回类型.

I have some code that uses template conversion operator to find return type of function found through ADL.

简化的代码如下:

#include <type_traits>

template<typename S>
struct probe {
    template<typename T, typename U = S, std::enable_if_t<
        std::is_same<T&, U>::value &&
        !std::is_const<T>::value, int> = 0>
    operator T& ();

    template<typename T, typename U = S&&, std::enable_if_t<
        std::is_same<T&&, U>::value &&
        !std::is_const<T>::value, int> = 0>
    operator T&& ();

    template<typename T, typename U = S, std::enable_if_t<
        std::is_same<T const&, U>::value, int> = 0>
    operator T const& () const;

    template<typename T, typename U = S&&, std::enable_if_t<
        std::is_same<T const&&, U>::value, int> = 0>
    operator T const&& () const;
};

namespace foo {
    struct bar {};

    auto find_me(bar const&) -> int { return 0; } 
}

int main() {
    // That would be inside a template in my code.
    find_me(probe<foo::bar>{});
}

在clang 6和GCC中,以上代码进行编译.但是,在Clang 7中,它不再编译了!

In clang 6 and GCC, the above code compiles. However, in Clang 7, it doesn't compile anymore!

https://godbolt.org/z/Lfs3UH

如您所见,clang 6将调用解析为probe<foo::bar>::operator foo::bar&&<foo::bar, foo::bar&&, 0>(),但是clang 7失败,因为它尝试调用probe<foo::bar>::operator const foo::bar&&<const foo::bar, foo::bar&&, 0>()

As you can see, clang 6 resolve the call to probe<foo::bar>::operator foo::bar&&<foo::bar, foo::bar&&, 0>() but clang 7 fails because it tries to call probe<foo::bar>::operator const foo::bar&&<const foo::bar, foo::bar&&, 0>()

哪个编译器正确?标准中的规则是什么?这是Clang的新错误还是已解决?

Which compiler is right? What is the rule in the standard for this? Is this a new Clang bug or is it a fix?

我想检查很多情况.不仅foo::bar作为参数,而且还有许多引用类型,例如:

There are many cases I want to check. Not just foo::bar as parameter, but many reference types, such as this:

namespace foo {
    struct bar {};

    auto find_me(bar const&) -> int { return 0; } 
    auto find_me(bar&&) -> int { return 0; } 
    auto find_me(bar const&&) -> int { return 0; } 
    auto find_me(bar&) -> int { return 0; } 
}

int main() {
    find_me(probe<foo::bar>{});
    find_me(probe<foo::bar&>{});
    find_me(probe<foo::bar&&>{});
    find_me(probe<foo::bar const&>{});
    find_me(probe<foo::bar const&&>{});
}

解析正确的函数调用很重要.

Resolving to the right function call is important.

这是所有这些案例的生动示例,GCC成功,但clang失败: https://godbolt.org/z /yrDFMg

Here's a live example of all those cases, GCC succeed but clang fails: https://godbolt.org/z/yrDFMg

推荐答案

此简化示例代码说明了clang 6/7和gcc之间的行为差​​异:

The difference in behavior between clang 6/7 and gcc is illustrated by this simplified example code:

#include <type_traits>

struct S{
    template<class T,class=std::enable_if_t<!std::is_const_v<T>>>
    operator T& ();
};

void test() {
    S a;
    const int& i = a; //Accepted by Gcc and clang 6 accept, rejected by clang 7
}

Gcc和Clang 6接受该代码,而clang 7拒绝该代码.

Gcc and Clang 6 accept the code, and clang 7 rejects it.

在Gcc的情况下,T=intT=const int均被视为情况.仅适用于clang 7 T=const int.因为T=const int被禁用,所以clang 7拒绝了该代码.

In the case of Gcc both T=int and T=const int are considered cases. For clang 7 only T=const int. Because T=const int is disabled, clang 7 reject the code.

根据 [over.match.ref] :

考虑S的转换函数及其基类. 那些未隐藏在S中且产生类型对cv2 T2的左值引用"(在初始化对函数的左值引用或右值引用时)或"cv2 T2"或对cv2 T2的右值引用"的非显式转换函数(当将右值引用或左值引用初始化为函数),其中"cv1 T"与"cv2 T2"的引用兼容)是候选函数. 对于直接初始化,未隐藏在S中的那些显式转换函数分别产生对cv2 T2的左值引用"或对cv2 T2的右值引用"或对cv2 T2的右值引用",其中T2与T的类型相同或可以通过资格转换转换为T型,也是候选函数.

The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type "lvalue reference to cv2 T2" (when initializing an lvalue reference or an rvalue reference to function) or "cv2 T2" or "rvalue reference to cv2 T2" (when initializing an rvalue reference or an lvalue reference to function), where "cv1 T" is reference-compatible with "cv2 T2", are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type "lvalue reference to cv2 T2" or "cv2 T2" or "rvalue reference to cv2 T2", respectively, where T2 is the same type as T or can be converted to type T with a qualification conversion, are also candidate functions.

在我们的案例中,这意味着可以将S转换为int&const int&.

In our case this means that convertion of S to int& or const int& could be candidate.

[temp.deduct.conv] :

模板参数推导是通过将转换函数模板的返回类型(称为P)与作为转换结果(称为A;请参阅[dcl [init.],[over.match.conv]和[over.match.ref](用于确定该类型),如[temp.deduct.type]中所述.

Template argument deduction is done by comparing the return type of the conversion function template (call it P) with the type that is required as the result of the conversion (call it A; see [dcl.init], [over.match.conv], and [over.match.ref] for the determination of that type) as described in [temp.deduct.type].

所以我认为可以接受两个字面意思:

So I think two literal readings are acceptable:

  1. gcc认为转换结果并不意味着转换序列的结果,因此首先根据[over.match.ref],然后为所有可能的转换序列的转换操作符执行模板参数推导.

  1. gcc considers that the result of the conversion does not mean the result of the conversion sequence, so it first decides that which conversion sequence are acceptable according to [over.match.ref] and then perform template argument deduction for the conversion operator for all the possible conversion sequences.

clang认为转换结果确实是转换序列的目标.并且它仅对T=cont int执行参数推论.

clang considers that the result of the conversion does mean the target of the conversion sequence. And it performs argument deduction only for T=cont int.

根据我在标准中所读的内容,我不能说什么是标准的正确"解释.尽管如此,我认为clang的行为通常与模板参数推导更一致:

From what I have read in the standard I cannot say what is the "right" interpretation of the standard. Nevertheless I think that clang behavior is more consistent with template argument deduction in general:

template<class T,class=std::enable_if_t<std::is_const_v<T>>>
void f(T& x);

void test(){
  int i;
  f(i);
  // If considering that the argument type is int caused
  // template argument deduction failure, then template argument
  // deduction would be performed for a const int argument.
  // But template argument deduction succeeds. So T is deduced to int. 
  // Only after this deduction template argument substitution happens.
  // => both gcc and clang reject this code.
  }

这篇关于clang 6和clang 7之间的模板转换运算符区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆