Pertanyaan Ambil sebagai parameter hanya objek yang operator + didefinisikan dalam C ++


Bagaimana saya bisa mengambil parameter ke objek hanya fungsi (dan jenis, seperti int, double atau float) yang operator + didefinisikan dalam C + +?

Saya hampir sepenuhnya yakin bahwa trik ini harus dilakukan dengan bantuan template, tetapi karena saya sangat baru untuk C ++, saya tidak dapat menemukannya sendiri.

Contoh deklarasi fungsi seperti itu akan sangat bagus.


8
2017-10-19 14:05


asal


Jawaban:


Solusi paling mudah, dengan decltype

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

Jika Anda melewatkan dua objek yang tidak dapat ditambahkan, decltype akan terbentuk buruk (seperti halnya fungsi-templatedefinisi).


Ini diterima, jadi saya merasa berkewajiban untuk memperkenalkan peningkatan substansial. Mari tambahkan penerusan sempurna, seperti yang disarankan oleh komentar:

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

Cukup mudah juga, meski agak verbose. Belok t dan u ke meneruskan referensi. Kemudian gunakan std::forward untuk memastikan kategori nilai referensi dipertahankan.


6
2017-10-19 14:08



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

sekarang can_add<int, int> adalah (diwarisi dari) std::true_type, sementara can_add<std::string, void*> adalah (diwarisi dari) std::false_type.

Sekarang, jika Anda menggunakan non-MSVC  kompilator, ini berfungsi:

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
}

atau

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
}

Di  proposal konsep akan membuat banyak pembersih sintaks ini.

Ada proposal untuk is_detected yang berfungsi seperti saya can_apply atas.

Itu declval bagian dari add_result menyebalkan. Singkat konsep, saya tidak tahu cara menghapusnya dengan bersih.


5
2017-10-19 14:26



Jika Anda ingin mengekstrak ini ke metafungsi detektor terpisah, itu bisa terlihat seperti

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

Kemudian, Anda dapat melakukan sfinae-membatasi fungsi Anda:

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

Ini adalah cara C ++ 03. Yakk disajikan solusi C ++ 14 modern.


4
2017-10-19 14:15