【问题标题】:Take as parameters only objects for which the operator+ is defined in C++仅将在 C++ 中定义了 operator+ 的对象作为参数
【发布时间】:2018-03-31 14:30:59
【问题描述】:

我怎样才能将在 C++ 中定义了 operator+ 的对象(以及类型,如 int、double 或 float)作为函数的参数?

我几乎绝对肯定这个技巧应该在模板的帮助下以某种方式完成,但由于我对 C++ 非常陌生,我无法自己弄清楚。

这样的函数声明的例子会非常好。

【问题讨论】:

  • 你想做什么?你能展示你的一些研究吗?
  • 你可以只使用一个模板并且假设它有操作符。在您尝试使用它时,它将无法编译。这对你来说足够了吗?
  • 您的问题看起来像是stackoverflow.com/questions/6534041/… 的变体,但我更喜欢@Kevin 建议的上述方法。如果您尝试添加没有“+”运算符的数据类型,编译器会捕获它

标签: c++ function templates parameter-passing


【解决方案1】:

最直接的解决方案,decltype

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

如果您传递了两个无法添加的对象,decltype 的格式将不正确(以及 function-template 的定义)。


这已被接受,因此我觉得有义务进行实质性改进。让我们按照 cmets 的建议添加完美转发:

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

虽然有些冗长,但也很简单。将tu 转为转发引用。然后使用std::forward 确保引用的值类别被保留。

【讨论】:

  • 在这个例子中你甚至不必使用decltype。只需return t+u 就足够了。我认为这个问题的难点在于执行实际的求和。
  • @luk32 - 我在 C++11 中确实需要它。
  • 如果结果是另一种类型怎么办?例如。 void?我认为你过早地假设了这些要求。有趣的是,只允许可求和类型,而不进行运行时求和。 OP从未说过,他们想要总结任何东西......
  • 是的,他们没有。 OP 没有对正文和返回类型提出任何要求,这是您的解释使问题变得简单。我只是说,这远非一般情况。
  • @RichardHodges - 想要,但认为它带来的依赖与帖子不太相关。如果没有更好的答案,我会在某个时候修改它。
【解决方案2】:
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&lt;int, int&gt; 是(继承自)std::true_type,而can_add&lt;std::string, void*&gt; 是(继承自)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 的提案,它的作用类似于我上面的can_apply

add_resultdeclval 部分很烦人。缺乏概念,我不知道如何干净地删除它。

【讨论】:

【解决方案3】:

如果您想将其提取到单独的检测器元函数中,它可能看起来像

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 的方式。 Yakk presented 一个现代 C++14 解决方案。

【讨论】:

  • 这读起来像旧的 C++03 测试表达式合法性的方法。
  • @Yakk 如果您知道更好的方法,请提供答案,我会很高兴地支持它。现在,你的非建设性批评毫无用处。
  • 这是非建设性的批评,而不是非建设性的批评。这将是建设性的批评。我只是说这让我想起了在更现代的机器出现之前这样做的方式。它工作得很好(尽管拥有std::integral_constant&lt;bool, ?&gt; 的全部功能与constexpr 转换和() 通常很好)
  • 不需要太敏感。我敢打赌 Yakk 不是在批评,而是在回忆。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-23
  • 2016-01-15
  • 2015-02-20
  • 2011-02-17
  • 1970-01-01
  • 2011-05-31
  • 2017-09-25
相关资源
最近更新 更多