brightside90
brightside90

Reputation: 243

noexcept for different operations with template type inside function

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

Answers (3)

YSC
YSC

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

Smeeheey
Smeeheey

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

Jodocus
Jodocus

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

Related Questions