更改类型参数名称时模板替换失败 - 编译器错误?
Template substitution fails when I change the type parameter name - compiler bug?
以下大部分代码摘自 Piotr Skotnicki 的 。我正在试验它并发现了我认为是 MSVC 14.0 更新 3 中的错误。
考虑以下代码:
#include <iostream>
template <typename T>
struct identity { using type = T; };
template <typename...>
using void_t = void;
template <typename F>
struct call_operator;
template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...)> : identity<R(A...)> {};
template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...) const> : identity<R(A...)> {};
template <typename F>
using call_operator_t = typename call_operator<F>::type;
template <typename, typename = void_t<>>
struct is_convertible_to_function
: std::false_type {};
template <typename L>
struct is_convertible_to_function<L, void_t<decltype(&L::operator())>>
: std::is_assignable<call_operator_t<decltype(&L::operator())>*&, L> {};
template <typename, typename = void_t<>>
struct is_callable_object
: std::false_type {};
template <typename L>
struct is_callable_object<L, void_t<decltype(&L::operator())>>
: std::true_type {};
int main()
{
auto x = []() {};
std::cout << std::boolalpha << is_callable_object<decltype(x)>::value;
std::getchar();
}
这会按预期打印 true
,因为编译器生成的 lambda 对象确实实现了 operator()
.
现在让我们将 is_callable_object
中的类型参数名称从 L
更改为 T
(与 is_convertible_to_function
中使用的类型名称不同的任何内容都会导致此问题,从什么我明白了)。
template <typename, typename = void_t<>>
struct is_callable_object
: std::false_type {};
template <typename T>
struct is_callable_object<T, void_t<decltype(&T::operator())>>
: std::true_type {};
突然,这会打印出 false
。 is_convertible_to_funtion
应该无关紧要,因为 is_callable_object
不以任何方式依赖它;事实上,如果我删除 is_convertible_to_function
,这个问题就会消失——我可以使用我想要的任何类型名称。
正如我所说,我怀疑这是一个错误,所以我问这个问题是为了确保这不是 C++ 标准中的一些奇怪行为;解决方法很简单。
表达式 sfinae 在 msvc 2015 中不起作用。它起作用的任何情况都是偶然的,不要相信它。 decltype
不能可靠地用于在 msvc 2015 中引起 sfinae。
停下来,走开,寻找其他解决方案。也许试试编译器内在函数。
不要假设参数名称匹配时的意外工作意味着其他任何东西都可以工作,或者它将在稍微不同的程序中工作。
您的解决方法不可信。
以下大部分代码摘自 Piotr Skotnicki 的
考虑以下代码:
#include <iostream>
template <typename T>
struct identity { using type = T; };
template <typename...>
using void_t = void;
template <typename F>
struct call_operator;
template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...)> : identity<R(A...)> {};
template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...) const> : identity<R(A...)> {};
template <typename F>
using call_operator_t = typename call_operator<F>::type;
template <typename, typename = void_t<>>
struct is_convertible_to_function
: std::false_type {};
template <typename L>
struct is_convertible_to_function<L, void_t<decltype(&L::operator())>>
: std::is_assignable<call_operator_t<decltype(&L::operator())>*&, L> {};
template <typename, typename = void_t<>>
struct is_callable_object
: std::false_type {};
template <typename L>
struct is_callable_object<L, void_t<decltype(&L::operator())>>
: std::true_type {};
int main()
{
auto x = []() {};
std::cout << std::boolalpha << is_callable_object<decltype(x)>::value;
std::getchar();
}
这会按预期打印 true
,因为编译器生成的 lambda 对象确实实现了 operator()
.
现在让我们将 is_callable_object
中的类型参数名称从 L
更改为 T
(与 is_convertible_to_function
中使用的类型名称不同的任何内容都会导致此问题,从什么我明白了)。
template <typename, typename = void_t<>>
struct is_callable_object
: std::false_type {};
template <typename T>
struct is_callable_object<T, void_t<decltype(&T::operator())>>
: std::true_type {};
突然,这会打印出 false
。 is_convertible_to_funtion
应该无关紧要,因为 is_callable_object
不以任何方式依赖它;事实上,如果我删除 is_convertible_to_function
,这个问题就会消失——我可以使用我想要的任何类型名称。
正如我所说,我怀疑这是一个错误,所以我问这个问题是为了确保这不是 C++ 标准中的一些奇怪行为;解决方法很简单。
表达式 sfinae 在 msvc 2015 中不起作用。它起作用的任何情况都是偶然的,不要相信它。 decltype
不能可靠地用于在 msvc 2015 中引起 sfinae。
停下来,走开,寻找其他解决方案。也许试试编译器内在函数。
不要假设参数名称匹配时的意外工作意味着其他任何东西都可以工作,或者它将在稍微不同的程序中工作。
您的解决方法不可信。