6

我不得不做一个类似的代码:

#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 上编译,并且会出现不明确的分辨率错误。哪个编译器错了,为什么?

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
4

1 回答 1

1

代码可以简化为以下内容

#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, "");
}

根据 [temp.deduct.conv]/ 5 & 6

通常,推导过程试图找到使推导的 A 与 A 相同的模板参数值。但是,有四种情况允许存在差异:

  • 如果原始 A 是引用类型,则 A 可以比推导的 A 更具 cv 限定(即引用所指的类型)

  • ...

仅当类型推导失败时才考虑这些替代方案。如果它们产生多个可能的推导 A,则类型推导失败。

T被推断为some_type用于两个函数调用。然后根据 [over.ics.rank]/ 3.3

如果用户定义的转换序列 U1 包含相同的用户定义的转换函数或构造函数,或者它们在聚合初始化中初始化相同的类,并且在任一情况下为第二个标准转换序列,则用户定义的转换序列 U1 是比另一个用户定义的转换序列 U2 更好的转换序列U1的第二个标准转换序列优于U2的第二个标准转换序列。

probe -> some_type& -> some_type&优于probe -> some_type& -> const some_type&,所以没有歧义,GCC 和 Clang 是对的。


顺便说一句,如果我们删除std::enable_if_t<...>上面代码中的一部分,MSVC 和 GCC 在 Clang 编译时会失败。为了进一步分析,我将重点放在第一个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{});
}

然后我们只在 Clang 下static_assert找到火灾。也就是说,Clang 推断为 be而不是。我认为这是 Clang 的错误。Tsome_typeconst some_type

于 2018-06-29T10:15:43.063 回答