Reputation: 243
I want to write a template function foo, that do some operations on type T, and inside this function values of type T can be:
So, I need to specify noexcept for this function with restrictions I've mentioned above.
Here is my code, but it isn't work properly:
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
What should I do, to make it work right?
Upvotes: 0
Views: 1193
Reputation: 40100
You try to check the existence of a T operator+(T,T)
with noexcept()
, but this will only detect noexcept operator+
. In my opinion, we can do better.
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))
{};
This defines a trait to check if, for a given type T
, the expression T{}+T{}
bears meaning.
Test cases:
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
}
You can then define:
template<class T>
struct is_foo_compatible : std::conjunction<
can_add<T>,
std::is_copy_constructible<T>,
std::is_assignable<T&, T>
> {};
And It Just Works™.
Upvotes: 1
Reputation: 10336
The problem is with your std::is_assignable<int, int>
(which is false
). You cannot in general assign to an int
, only to an int&
(i.e. an l-value reference, which implies the existence of an actual object that you're modifying). Without the reference qualifier, r-values would have to be assignable as well (for example the statement 1=1
would have to be valid).
So change your T
to a T&
in the std::is_assignable
expression.
Upvotes: 0
Reputation: 7601
noexcept(foo<int>());
is false because std::is_assignable<int, int>::value
is false, e.g. you cannot write 1 = 1
. What you might have wanted to do is to use std::is_assignable<T&, T>
instead.
Upvotes: 4