我必须执行类似的代码:
#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& () const;
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;
};
struct some_type {};
struct other_type {};
auto test_call(some_type const&, other_type) -> std::false_type;
auto test_call(some_type&, other_type) -> std::true_type;
int main() {
static_assert(decltype(test_call(probe<some_type&>{}, other_type{}))::value, "");
}
它可以在GCC和Clang下运行,但是不能在Visual Studio上编译,解析度模棱两可。哪个编译器是错误的,为什么?
这是msvc输出:
source_file.cpp(31): error C2668: 'test_call': ambiguous call to overloaded function source_file.cpp(28): note: could be 'std::true_type test_call(some_type &,other_type)' source_file.cpp(27): note: or 'std::false_type test_call(const some_type &,other_type)' source_file.cpp(31): note: while trying to match the argument list '(probe<some_type &>, other_type)' source_file.cpp(31): error C2651: 'unknown-type': left of '::' must be a class, struct or union source_file.cpp(31): error C2062: type 'unknown-type' unexpected
答案 0 :(得分:1)
代码可以简化为the following:
#include <type_traits>
struct some_type {};
struct probe {
template<typename T, std::enable_if_t<!std::is_const<T>::value, int> = 0>
operator T& () const;
};
auto test_call(some_type const&) -> std::false_type;
auto test_call(some_type&) -> std::true_type;
int main() {
static_assert(decltype(test_call(probe{}))::value, "");
}
对于两个函数调用,通常,推论过程会尝试查找使推论的A与A相同的模板参数值。但是,有四种情况允许不同:
如果原始A是引用类型,则A可以比推导的A(即引用所引用的类型)具有更高的简历资格
...
仅当类型推断否则会失败时,才考虑这些替代方法。如果它们产生多个可能的推导A,则类型推导将失败。
T
均推导为some_type
。然后根据[over.ics.rank] / 3.3:
如果用户定义的转换序列U1包含相同的用户定义的转换函数或构造函数,或者它们在聚合初始化中初始化了相同的类,则它们比另一个用户定义的转换序列U2更好。 U1的标准转换顺序要好于U2的第二个标准转换顺序。
probe -> some_type& -> some_type&
比probe -> some_type& -> const some_type&
更好,因此没有歧义,GCC和Clang是正确的。
顺便说一句,如果我们在上面的代码中删除std::enable_if_t<...>
部分,则在Clang编译时MSVC和GCC失败。为了进一步分析,我重点介绍第一个test_all
:
#include <type_traits>
struct some_type {};
struct probe {
template<typename T>
operator T& () const
{
static_assert(std::is_const_v<T>);
static T t;
return t;
}
};
auto test_call(some_type const&) -> std::false_type;
int main() {
test_call(probe{});
}
然后我们发现static_assert
射击only under Clang。也就是说,Clang推论T
为some_type
而不是const some_type
。我认为这是Clang的错误。