【发布时间】:2018-10-04 17:06:21
【问题描述】:
我有一些代码使用模板转换运算符来查找通过 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 中,它不再编译了!
如您所见,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>()
哪个编译器是正确的?标准中的规则是什么?这是新的 Clang 错误还是已修复?
我想检查很多情况。不仅仅是foo::bar作为参数,还有很多引用类型,比如这个:
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&&>{});
}
解决正确的函数调用很重要。
这是所有这些情况的一个活生生的例子,GCC 成功但 clang 失败:https://godbolt.org/z/yrDFMg
【问题讨论】:
-
奇怪,但如果你将
std::is_same<T const&&, U>更改为std::is_same<T&&, U>它将起作用。我认为这个 C++ 17 特性。 godbolt.org/z/ZO7pfC -
@VictorGubin 是的,但这有点作弊,在这种情况下不会起作用:godbolt.org/z/0q4WOP
-
哦,那可能是另一个clang错误? godbolt.org/z/USjQPD
-
我删除了答案,因为我真的对 [temp.deduct.conv] 感到困惑并且不知道如何应用这些规则,它们可能是相关的。
标签: c++ templates clang language-lawyer conversion-operator