8

How can I take as parameter to the function only objects (and types, like int, double or float) for which the operator+ is defined in C++?

I am almost absolutely sure that the trick should be done somehow with the help of the template, but since I am very new to C++ I can not figure it by myself.

The example of such a function declaration would be very nice.

trafalgarLaww
  • 505
  • 6
  • 15
  • What are you trying to do ? Can you show some of your research ? – rak007 Oct 19 '17 at 14:07
  • 2
    You can just use a template and *assume* it has the operator. It will fail to compile at the point you try to use it. Is that good enough for you? – Kevin Oct 19 '17 at 14:08
  • Your question looks like a variation on https://stackoverflow.com/questions/6534041/how-to-check-whether-operator-exists, but I like the approach @Kevin suggests above better. The compiler will catch it if you try to add a data type the doesn't have the "+" operator – Joshua Clayton Nov 08 '17 at 00:28

3 Answers3

6

The most straightforward solution, with decltype

template<typename T, typename U>
auto func(T const& t, U const& u) -> decltype(t + u) {
    return t + u;
}

If you pass two objects which cannot be added, decltype will be ill-formed (as well as the function-template's definition).


This was accepted, so I feel obligated to introduce a substantial improvement. Let's add perfect forwarding, as the comments suggested:

#include <utility>

template<typename T, typename U>
auto func(T&& t, U&& u) -> decltype(std::forward<T>(t) + std::forward<U>(u)) {
    return std::forward<T>(t) + std::forward<U>(u);
}

Pretty straightforward as well, despite being somewhat verbose. Turn t and u into forwarding references. Then use std::forward to make sure the value categories of the references are being preserved.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    You don't even have to use `decltype` for this example. Just `return t+u` would suffice. I think the hard part of this question is executing actual summation. – luk32 Oct 19 '17 at 14:10
  • 1
    @luk32 - I do need it in C++11. – StoryTeller - Unslander Monica Oct 19 '17 at 14:10
  • 2
    What if another type is meant as a result? E.g. `void`? I think you assumed the requirements prematurely. Interesting would be, just to allow summable types, without doing the run-time summation. OP never said, they want to sum anything... – luk32 Oct 19 '17 at 14:12
  • @luk32 - I think I understood the OP well. A function declaration that accepts two objects which have `operator +` defined. Both the return type and the body check for it separately. The OP didn't specify they wanted to *add them* either, did they? – StoryTeller - Unslander Monica Oct 19 '17 at 14:15
  • 1
    Yes, they didn't. OP didn't make any requirements about the body, and the return type, it's your interpretation which makes the question trivially easy. I am just saying, it is far from general case. – luk32 Oct 19 '17 at 14:18
  • @luk32 - You are free to post another approach. I'm sure it will fit the OP's request for an example. If your aim is for me to delete this post, I don't see why you bother. – StoryTeller - Unslander Monica Oct 19 '17 at 14:19
  • My point is certainly not to make you delete your answer. It's good if you want to sum the stuff, not just accept summable stuff. I just point out you trivialized it. Don't you agree writing `t+u` for the compiler to evalute the expression makes it simple? – luk32 Oct 19 '17 at 14:25
  • @luk32 - The compiler *will have* to evaluate the expression at some point or another. I'd be impressed if you can write a check for it that doesn't mention the `+` token at all. – StoryTeller - Unslander Monica Oct 19 '17 at 14:28
  • 1
    @RichardHodges - Wanted to, but figured the dependency it brings isn't too relevant to the post. I'll revise it at some-point, if no better answer comes up. – StoryTeller - Unslander Monica Oct 19 '17 at 14:29
5
namespace details {
  template<template<class...>class, class, class...>
  struct can_apply:std::false_type{};

  // C++17 has void_t.  It is useful.  Here is a short implementation:
  template<class...>struct voider{using type=void;};
  template<class...Ts>using void_t=typename voider<Ts...>::type;

  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;

template<class A, class B>
using add_result = decltype( std::declval<A>()+std::declval<B>() );

template<class A, class B>
using can_add = can_apply< add_result, A, B >;

now can_add<int, int> is (inherited from) std::true_type, while can_add<std::string, void*> is (inherited from) std::false_type.

Now, if you are using a non-MSVC compiler, this works:

template<class A, class B,
  std::enable_if_t<can_add<A&,B&>{}, int> =0
>
void only_addable( A a, B b ) {
  a+b; // guaranteed to compile, if not link
}

or

template<class A, class B>
std::enable_if_t<can_add<A&,B&>{}, void> // void is return value
only_addable( A a, B b ) {
  a+b; // guaranteed to compile, if not link
}

In the concept proposal will make much of this syntax cleaner.

There is a proposal for is_detected which works like my can_apply above.

The declval part of the add_result is annoying. Short of concepts, I'm unaware of how to remove it cleanly.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Didn't notice before that this trick is mentioned here: http://en.cppreference.com/w/cpp/types/void_t. Great answer! – lisyarus Oct 19 '17 at 14:51
4

If you want to extract this to a separate detector metafunction, it could look like

template <typename T>
struct is_addable
{
private:

    template <typename A, typename B = decltype(std::declval<A>() + std::declval<A>())>
    static std::true_type check (A *);

    template <typename A>
    static std::false_type check (...);

public:

    static constexpr bool value = decltype(check<T>(nullptr))::value;
};

Then, you can sfinae-constrain your function:

template <typename A, typename = std::enable_if_t<is_addable<A>::value>>
void func (A x, A y)
{
    // ...
}

This is the C++03 way. Yakk presented a modern C++14 solution.

lisyarus
  • 15,025
  • 3
  • 43
  • 68
  • This reads like old C++03 ways of testing for the legality of an expression. – Yakk - Adam Nevraumont Oct 19 '17 at 14:15
  • @Yakk If you know a better way, provide an answer with it, and I'll happily upvote it. Right now, your unconstructive critics is useless. – lisyarus Oct 19 '17 at 14:17
  • 1
    It is unconstructive criticism, not unconstructive critics. Which would qualify as constructive criticism. I'm just saying it reminds me of the way this was done before more modern machinery existed. It works just fine (although, having the full power of `std::integral_constant` with `constexpr` conversion and `()` is often nice) – Yakk - Adam Nevraumont Oct 19 '17 at 14:20
  • No need to be touchy. I'd wager Yakk wasn't critiquing, but was reminiscing. – StoryTeller - Unslander Monica Oct 19 '17 at 14:20