Как я могу принять в качестве параметра к функции только объекты (и типы, такие как int, double или float), для которых оператор + определен в C ++?

Я почти абсолютно уверен, что трюк должен быть каким-то образом сделан с помощью шаблона, но, поскольку я очень новичок в C ++, я не могу понять это сам.

Пример такого объявления функции был бы очень хорош.

8
trafalgarLaww 19 Окт 2017 в 17:05

3 ответа

Лучший ответ

Самое простое решение с decltype

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

Если вы передадите два объекта, которые не могут быть добавлены, decltype будет неправильно сформирован (как и определение function-template ).


Это было принято, поэтому я чувствую себя обязанным внести существенное улучшение. Давайте добавим идеальную пересылку, как предложили комментарии:

#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);
}

Довольно просто, хотя и несколько многословно. Превратите t и u в пересылку ссылок . Затем используйте std::forward, чтобы убедиться, что значения категорий ссылок сохраняются.

6
StoryTeller - Unslander Monica 19 Окт 2017 в 17:40
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 >;

Теперь can_add<int, int> (унаследовано от) std::true_type, а can_add<std::string, void*> - (унаследовано от) std::false_type.

Теперь, если вы используете не-MSVC , это работает:

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
}

Или

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
}

В Концептуальное предложение сделает большую часть этого синтаксиса чище.

Существует предложение для is_detected, которое работает как мой {{ X1}} выше.

declval часть add_result раздражает. Если не считать понятий, я не знаю, как удалить это чисто.

5
Yakk - Adam Nevraumont 19 Окт 2017 в 14:53

Если вы хотите извлечь это в отдельную метафункцию детектора, это может выглядеть так:

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;
};

Затем вы можете sfinae-ограничить вашу функцию:

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

Это путь C ++ 03. Якк представил современное решение C ++ 14.

4
lisyarus 19 Окт 2017 в 15:05