noexcept 对于函数内部模板类型的不同操作
noexcept for different operations with template type inside function
我想写一个模板函数 foo,对类型 T 做一些操作,在这个函数中类型 T 的值可以是:
- 已复制
- 已分配
- 用运算符求和+
所以,我需要为这个函数指定 noexcept,并带有我上面提到的限制。
这是我的代码,但它不能正常工作:
template<class T>
void foo()
noexcept(noexcept(std::declval<T>() + std::declval<T>()) && std::is_copy_constructible<T>::value && std::is_assignable<T, T>::value)
{}
bool b1 = noexcept(foo<int>()); // false, but should return true
bool b2 = noexcept(foo<std::string>()); // false
我应该怎么做才能让它正常工作?
noexcept(foo<int>());
为假,因为 std::is_assignable<int, int>::value
为假,例如你不能写 1 = 1
。您可能想要做的是改用 std::is_assignable<T&, T>
。
问题出在您的 std::is_assignable<int, int>
(即 false
)。您通常不能分配给 int
,只能分配给 int&
(即左值引用,这意味着存在您正在修改的实际对象)。如果没有引用限定符,右值也必须是可分配的(例如语句 1=1
必须有效)。
因此,将 T
更改为 std::is_assignable
表达式中的 T&
。
您尝试用 noexcept()
检查 T operator+(T,T)
是否存在,但这只会检测到 noexcept operator+
。在我看来,我们可以做得更好。
namespace detail
{
template<class>
struct sfinae_true : std::true_type{};
template<class T>
static auto can_add(int) -> sfinae_true<decltype(std::declval<T>() + std::declval<T>())>;
template<class>
static auto can_add(long) -> std::false_type;
}
template<class T>
struct can_add : decltype(detail::can_add<T>(0))
{};
这定义了一个特征来检查,对于给定的类型 T
,表达式 T{}+T{}
是否有意义。
测试用例:
struct s1 {};
s1 operator+(s1,s1) { return s1{}; }
struct s2 {};
s2 operator+(s2 const&, s2 const&) { return s2{}; }
struct s3 {};
#include <iostream>
int main()
{
std::cout << can_add<s1>::value << "\n"; // true
std::cout << can_add<s2>::value << "\n"; // true
std::cout << can_add<s3>::value << "\n"; // false
}
然后您可以定义:
template<class T>
struct is_foo_compatible : std::conjunction<
can_add<T>,
std::is_copy_constructible<T>,
std::is_assignable<T&, T>
> {};
它就是有效™。
我想写一个模板函数 foo,对类型 T 做一些操作,在这个函数中类型 T 的值可以是:
- 已复制
- 已分配
- 用运算符求和+
所以,我需要为这个函数指定 noexcept,并带有我上面提到的限制。
这是我的代码,但它不能正常工作:
template<class T>
void foo()
noexcept(noexcept(std::declval<T>() + std::declval<T>()) && std::is_copy_constructible<T>::value && std::is_assignable<T, T>::value)
{}
bool b1 = noexcept(foo<int>()); // false, but should return true
bool b2 = noexcept(foo<std::string>()); // false
我应该怎么做才能让它正常工作?
noexcept(foo<int>());
为假,因为 std::is_assignable<int, int>::value
为假,例如你不能写 1 = 1
。您可能想要做的是改用 std::is_assignable<T&, T>
。
问题出在您的 std::is_assignable<int, int>
(即 false
)。您通常不能分配给 int
,只能分配给 int&
(即左值引用,这意味着存在您正在修改的实际对象)。如果没有引用限定符,右值也必须是可分配的(例如语句 1=1
必须有效)。
因此,将 T
更改为 std::is_assignable
表达式中的 T&
。
您尝试用 noexcept()
检查 T operator+(T,T)
是否存在,但这只会检测到 noexcept operator+
。在我看来,我们可以做得更好。
namespace detail
{
template<class>
struct sfinae_true : std::true_type{};
template<class T>
static auto can_add(int) -> sfinae_true<decltype(std::declval<T>() + std::declval<T>())>;
template<class>
static auto can_add(long) -> std::false_type;
}
template<class T>
struct can_add : decltype(detail::can_add<T>(0))
{};
这定义了一个特征来检查,对于给定的类型 T
,表达式 T{}+T{}
是否有意义。
测试用例:
struct s1 {};
s1 operator+(s1,s1) { return s1{}; }
struct s2 {};
s2 operator+(s2 const&, s2 const&) { return s2{}; }
struct s3 {};
#include <iostream>
int main()
{
std::cout << can_add<s1>::value << "\n"; // true
std::cout << can_add<s2>::value << "\n"; // true
std::cout << can_add<s3>::value << "\n"; // false
}
然后您可以定义:
template<class T>
struct is_foo_compatible : std::conjunction<
can_add<T>,
std::is_copy_constructible<T>,
std::is_assignable<T&, T>
> {};
它就是有效™。